diff --git a/src/dialogs/katedialogs.cpp b/src/dialogs/katedialogs.cpp index b88795ed..211ba4a4 100644 --- a/src/dialogs/katedialogs.cpp +++ b/src/dialogs/katedialogs.cpp @@ -1,1459 +1,1462 @@ /* This file is part of the KDE libraries Copyright (C) 2002, 2003 Anders Lund Copyright (C) 2003 Christoph Cullmann Copyright (C) 2001 Joseph Wenninger Copyright (C) 2006 Dominik Haumann Copyright (C) 2007 Mirko Stocker Copyright (C) 2009 Michel Ludwig Copyright (C) 2009 Erlend Hamberg Based on work of: Copyright (C) 1999 Jochen Wilhelmy This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ //BEGIN Includes #include "katedialogs.h" #include #include #include "kateautoindent.h" #include "katebuffer.h" #include "kateconfig.h" #include "katedocument.h" #include "kateglobal.h" #include "kateschema.h" #include "katemodeconfigpage.h" #include "kateview.h" #include "spellcheck/spellcheck.h" #include "kateglobal.h" // auto generated ui files #include "ui_textareaappearanceconfigwidget.h" #include "ui_bordersappearanceconfigwidget.h" #include "ui_navigationconfigwidget.h" #include "ui_editconfigwidget.h" #include "ui_indentationconfigwidget.h" #include "ui_completionconfigtab.h" #include "ui_opensaveconfigwidget.h" #include "ui_opensaveconfigadvwidget.h" #include "ui_spellcheckconfigwidget.h" #include #include #include #include #include #include #include "katepartdebug.h" #include "kateabstractinputmodefactory.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // trailing slash is important #define HLDOWNLOADPATH QStringLiteral("http://kate.kde.org/syntax/") //END //BEGIN KateIndentConfigTab KateIndentConfigTab::KateIndentConfigTab(QWidget *parent) : KateConfigPage(parent) { // This will let us have more separation between this page and // the QTabWidget edge (ereslibre) QVBoxLayout *layout = new QVBoxLayout; QWidget *newWidget = new QWidget(this); ui = new Ui::IndentationConfigWidget(); ui->setupUi(newWidget); ui->cmbMode->addItems(KateAutoIndent::listModes()); ui->label->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard); connect(ui->label, SIGNAL(linkActivated(QString)), this, SLOT(showWhatsThis(QString))); // What's This? help can be found in the ui file reload(); // // after initial reload, connect the stuff for the changed () signal // connect(ui->cmbMode, SIGNAL(activated(int)), this, SLOT(slotChanged())); connect(ui->rbIndentWithTabs, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(ui->rbIndentWithSpaces, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(ui->rbIndentMixed, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(ui->rbIndentWithTabs, SIGNAL(toggled(bool)), ui->sbIndentWidth, SLOT(setDisabled(bool))); connect(ui->chkKeepExtraSpaces, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(ui->chkIndentPaste, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(ui->chkBackspaceUnindents, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(ui->sbTabWidth, SIGNAL(valueChanged(int)), this, SLOT(slotChanged())); connect(ui->sbIndentWidth, SIGNAL(valueChanged(int)), this, SLOT(slotChanged())); connect(ui->rbTabAdvances, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(ui->rbTabIndents, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(ui->rbTabSmart, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); layout->addWidget(newWidget); setLayout(layout); } KateIndentConfigTab::~KateIndentConfigTab() { delete ui; } void KateIndentConfigTab::slotChanged() { if (ui->rbIndentWithTabs->isChecked()) { ui->sbIndentWidth->setValue(ui->sbTabWidth->value()); } KateConfigPage::slotChanged(); } void KateIndentConfigTab::showWhatsThis(const QString &text) { QWhatsThis::showText(QCursor::pos(), text); } void KateIndentConfigTab::apply() { // nothing changed, no need to apply stuff if (!hasChanged()) { return; } m_changed = false; KateDocumentConfig::global()->configStart(); KateDocumentConfig::global()->setKeepExtraSpaces(ui->chkKeepExtraSpaces->isChecked()); KateDocumentConfig::global()->setBackspaceIndents(ui->chkBackspaceUnindents->isChecked()); KateDocumentConfig::global()->setIndentPastedText(ui->chkIndentPaste->isChecked()); KateDocumentConfig::global()->setIndentationWidth(ui->sbIndentWidth->value()); KateDocumentConfig::global()->setIndentationMode(KateAutoIndent::modeName(ui->cmbMode->currentIndex())); KateDocumentConfig::global()->setTabWidth(ui->sbTabWidth->value()); KateDocumentConfig::global()->setReplaceTabsDyn(ui->rbIndentWithSpaces->isChecked()); if (ui->rbTabAdvances->isChecked()) { KateDocumentConfig::global()->setTabHandling(KateDocumentConfig::tabInsertsTab); } else if (ui->rbTabIndents->isChecked()) { KateDocumentConfig::global()->setTabHandling(KateDocumentConfig::tabIndents); } else { KateDocumentConfig::global()->setTabHandling(KateDocumentConfig::tabSmart); } KateDocumentConfig::global()->configEnd(); } void KateIndentConfigTab::reload() { ui->sbTabWidth->setSuffix(ki18np(" character", " characters")); ui->sbTabWidth->setValue(KateDocumentConfig::global()->tabWidth()); ui->sbIndentWidth->setSuffix(ki18np(" character", " characters")); ui->sbIndentWidth->setValue(KateDocumentConfig::global()->indentationWidth()); ui->chkKeepExtraSpaces->setChecked(KateDocumentConfig::global()->keepExtraSpaces()); ui->chkIndentPaste->setChecked(KateDocumentConfig::global()->indentPastedText()); ui->chkBackspaceUnindents->setChecked(KateDocumentConfig::global()->backspaceIndents()); ui->rbTabAdvances->setChecked(KateDocumentConfig::global()->tabHandling() == KateDocumentConfig::tabInsertsTab); ui->rbTabIndents->setChecked(KateDocumentConfig::global()->tabHandling() == KateDocumentConfig::tabIndents); ui->rbTabSmart->setChecked(KateDocumentConfig::global()->tabHandling() == KateDocumentConfig::tabSmart); ui->cmbMode->setCurrentIndex(KateAutoIndent::modeNumber(KateDocumentConfig::global()->indentationMode())); if (KateDocumentConfig::global()->replaceTabsDyn()) { ui->rbIndentWithSpaces->setChecked(true); } else { if (KateDocumentConfig::global()->indentationWidth() == KateDocumentConfig::global()->tabWidth()) { ui->rbIndentWithTabs->setChecked(true); } else { ui->rbIndentMixed->setChecked(true); } } ui->sbIndentWidth->setEnabled(!ui->rbIndentWithTabs->isChecked()); } QString KateIndentConfigTab::name() const { return i18n("Indentation"); } //END KateIndentConfigTab //BEGIN KateCompletionConfigTab KateCompletionConfigTab::KateCompletionConfigTab(QWidget *parent) : KateConfigPage(parent) { // This will let us have more separation between this page and // the QTabWidget edge (ereslibre) QVBoxLayout *layout = new QVBoxLayout; QWidget *newWidget = new QWidget(this); ui = new Ui::CompletionConfigTab(); ui->setupUi(newWidget); // What's This? help can be found in the ui file reload(); // // after initial reload, connect the stuff for the changed () signal // connect(ui->chkAutoCompletionEnabled, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(ui->gbWordCompletion, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(ui->gbKeywordCompletion, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(ui->minimalWordLength, SIGNAL(valueChanged(int)), this, SLOT(slotChanged())); connect(ui->removeTail, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); layout->addWidget(newWidget); setLayout(layout); } KateCompletionConfigTab::~KateCompletionConfigTab() { delete ui; } void KateCompletionConfigTab::showWhatsThis(const QString &text) { QWhatsThis::showText(QCursor::pos(), text); } void KateCompletionConfigTab::apply() { // nothing changed, no need to apply stuff if (!hasChanged()) { return; } m_changed = false; KateViewConfig::global()->configStart(); KateViewConfig::global()->setAutomaticCompletionInvocation(ui->chkAutoCompletionEnabled->isChecked()); KateViewConfig::global()->setWordCompletion(ui->gbWordCompletion->isChecked()); KateViewConfig::global()->setWordCompletionMinimalWordLength(ui->minimalWordLength->value()); KateViewConfig::global()->setWordCompletionRemoveTail(ui->removeTail->isChecked()); KateViewConfig::global()->setKeywordCompletion(ui->gbKeywordCompletion->isChecked()); KateViewConfig::global()->configEnd(); } void KateCompletionConfigTab::reload() { ui->chkAutoCompletionEnabled->setChecked(KateViewConfig::global()->automaticCompletionInvocation()); ui->gbWordCompletion->setChecked(KateViewConfig::global()->wordCompletion()); ui->minimalWordLength->setValue(KateViewConfig::global()->wordCompletionMinimalWordLength()); ui->gbKeywordCompletion->setChecked(KateViewConfig::global()->keywordCompletion()); ui->removeTail->setChecked(KateViewConfig::global()->wordCompletionRemoveTail()); } QString KateCompletionConfigTab::name() const { return i18n("Auto Completion"); } //END KateCompletionConfigTab //BEGIN KateSpellCheckConfigTab KateSpellCheckConfigTab::KateSpellCheckConfigTab(QWidget *parent) : KateConfigPage(parent) { // This will let us have more separation between this page and // the QTabWidget edge (ereslibre) QVBoxLayout *layout = new QVBoxLayout; QWidget *newWidget = new QWidget(this); ui = new Ui::SpellCheckConfigWidget(); ui->setupUi(newWidget); // What's This? help can be found in the ui file reload(); // // after initial reload, connect the stuff for the changed () signal m_sonnetConfigWidget = new Sonnet::ConfigWidget(this); connect(m_sonnetConfigWidget, SIGNAL(configChanged()), this, SLOT(slotChanged())); layout->addWidget(m_sonnetConfigWidget); layout->addWidget(newWidget); setLayout(layout); } KateSpellCheckConfigTab::~KateSpellCheckConfigTab() { delete ui; } void KateSpellCheckConfigTab::showWhatsThis(const QString &text) { QWhatsThis::showText(QCursor::pos(), text); } void KateSpellCheckConfigTab::apply() { if (!hasChanged()) { // nothing changed, no need to apply stuff return; } m_changed = false; KateDocumentConfig::global()->configStart(); m_sonnetConfigWidget->save(); KateDocumentConfig::global()->configEnd(); foreach (KTextEditor::DocumentPrivate *doc, KTextEditor::EditorPrivate::self()->kateDocuments()) { doc->refreshOnTheFlyCheck(); } } void KateSpellCheckConfigTab::reload() { // does nothing } QString KateSpellCheckConfigTab::name() const { return i18n("Spellcheck"); } //END KateSpellCheckConfigTab //BEGIN KateNavigationConfigTab KateNavigationConfigTab::KateNavigationConfigTab(QWidget *parent) : KateConfigPage(parent) { // This will let us having more separation between this page and // the QTabWidget edge (ereslibre) QVBoxLayout *layout = new QVBoxLayout; QWidget *newWidget = new QWidget(this); ui = new Ui::NavigationConfigWidget(); ui->setupUi(newWidget); // What's This? Help is in the ui-files reload(); // // after initial reload, connect the stuff for the changed () signal // connect(ui->cbTextSelectionMode, SIGNAL(currentIndexChanged(int)), this, SLOT(slotChanged())); connect(ui->chkSmartHome, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(ui->chkPagingMovesCursor, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(ui->sbAutoCenterCursor, SIGNAL(valueChanged(int)), this, SLOT(slotChanged())); connect(ui->chkScrollPastEnd, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); layout->addWidget(newWidget); setLayout(layout); } KateNavigationConfigTab::~KateNavigationConfigTab() { delete ui; } void KateNavigationConfigTab::apply() { // nothing changed, no need to apply stuff if (!hasChanged()) { return; } m_changed = false; KateViewConfig::global()->configStart(); KateDocumentConfig::global()->configStart(); KateDocumentConfig::global()->setSmartHome(ui->chkSmartHome->isChecked()); KateViewConfig::global()->setAutoCenterLines(qMax(0, ui->sbAutoCenterCursor->value())); KateDocumentConfig::global()->setPageUpDownMovesCursor(ui->chkPagingMovesCursor->isChecked()); KateViewConfig::global()->setPersistentSelection(ui->cbTextSelectionMode->currentIndex() == 1); KateViewConfig::global()->setScrollPastEnd(ui->chkScrollPastEnd->isChecked()); KateDocumentConfig::global()->configEnd(); KateViewConfig::global()->configEnd(); } void KateNavigationConfigTab::reload() { ui->cbTextSelectionMode->setCurrentIndex(KateViewConfig::global()->persistentSelection() ? 1 : 0); ui->chkSmartHome->setChecked(KateDocumentConfig::global()->smartHome()); ui->chkPagingMovesCursor->setChecked(KateDocumentConfig::global()->pageUpDownMovesCursor()); ui->sbAutoCenterCursor->setValue(KateViewConfig::global()->autoCenterLines()); ui->chkScrollPastEnd->setChecked(KateViewConfig::global()->scrollPastEnd()); } QString KateNavigationConfigTab::name() const { return i18n("Text Navigation"); } //END KateNavigationConfigTab //BEGIN KateEditGeneralConfigTab KateEditGeneralConfigTab::KateEditGeneralConfigTab(QWidget *parent) : KateConfigPage(parent) { QVBoxLayout *layout = new QVBoxLayout; QWidget *newWidget = new QWidget(this); ui = new Ui::EditConfigWidget(); ui->setupUi(newWidget); QList inputModes = KTextEditor::EditorPrivate::self()->inputModeFactories(); Q_FOREACH(KateAbstractInputModeFactory *fact, inputModes) { ui->cmbInputMode->addItem(fact->name(), static_cast(fact->inputMode())); } reload(); connect(ui->chkStaticWordWrap, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(ui->chkShowStaticWordWrapMarker, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(ui->sbWordWrap, SIGNAL(valueChanged(int)), this, SLOT(slotChanged())); connect(ui->chkAutoBrackets, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(ui->chkSmartCopyCut, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(ui->cmbInputMode, SIGNAL(currentIndexChanged(int)), this, SLOT(slotChanged())); // "What's this?" help is in the ui-file layout->addWidget(newWidget); setLayout(layout); } KateEditGeneralConfigTab::~KateEditGeneralConfigTab() { delete ui; } void KateEditGeneralConfigTab::apply() { // nothing changed, no need to apply stuff if (!hasChanged()) { return; } m_changed = false; KateViewConfig::global()->configStart(); KateDocumentConfig::global()->configStart(); KateDocumentConfig::global()->setWordWrapAt(ui->sbWordWrap->value()); KateDocumentConfig::global()->setWordWrap(ui->chkStaticWordWrap->isChecked()); KateRendererConfig::global()->setWordWrapMarker(ui->chkShowStaticWordWrapMarker->isChecked()); KateViewConfig::global()->setAutoBrackets(ui->chkAutoBrackets->isChecked()); KateViewConfig::global()->setSmartCopyCut(ui->chkSmartCopyCut->isChecked()); KateViewConfig::global()->setInputModeRaw(ui->cmbInputMode->currentData().toInt()); KateDocumentConfig::global()->configEnd(); KateViewConfig::global()->configEnd(); } void KateEditGeneralConfigTab::reload() { ui->chkStaticWordWrap->setChecked(KateDocumentConfig::global()->wordWrap()); ui->chkShowStaticWordWrapMarker->setChecked(KateRendererConfig::global()->wordWrapMarker()); ui->sbWordWrap->setSuffix(ki18ncp("Wrap words at (value is at 20 or larger)", " character", " characters")); ui->sbWordWrap->setValue(KateDocumentConfig::global()->wordWrapAt()); ui->chkAutoBrackets->setChecked(KateViewConfig::global()->autoBrackets()); ui->chkSmartCopyCut->setChecked(KateViewConfig::global()->smartCopyCut()); const int id = static_cast(KateViewConfig::global()->inputMode()); ui->cmbInputMode->setCurrentIndex(ui->cmbInputMode->findData(id)); } QString KateEditGeneralConfigTab::name() const { return i18n("General"); } //END KateEditGeneralConfigTab //BEGIN KateEditConfigTab KateEditConfigTab::KateEditConfigTab(QWidget *parent) : KateConfigPage(parent) , editConfigTab(new KateEditGeneralConfigTab(this)) , navigationConfigTab(new KateNavigationConfigTab(this)) , indentConfigTab(new KateIndentConfigTab(this)) , completionConfigTab(new KateCompletionConfigTab(this)) , spellCheckConfigTab(new KateSpellCheckConfigTab(this)) { QVBoxLayout *layout = new QVBoxLayout; layout->setMargin(0); QTabWidget *tabWidget = new QTabWidget(this); // add all tabs tabWidget->insertTab(0, editConfigTab, editConfigTab->name()); tabWidget->insertTab(1, navigationConfigTab, navigationConfigTab->name()); tabWidget->insertTab(2, indentConfigTab, indentConfigTab->name()); tabWidget->insertTab(3, completionConfigTab, completionConfigTab->name()); tabWidget->insertTab(4, spellCheckConfigTab, spellCheckConfigTab->name()); connect(editConfigTab, SIGNAL(changed()), this, SLOT(slotChanged())); connect(navigationConfigTab, SIGNAL(changed()), this, SLOT(slotChanged())); connect(indentConfigTab, SIGNAL(changed()), this, SLOT(slotChanged())); connect(completionConfigTab, SIGNAL(changed()), this, SLOT(slotChanged())); connect(spellCheckConfigTab, SIGNAL(changed()), this, SLOT(slotChanged())); int i = tabWidget->count(); Q_FOREACH(KateAbstractInputModeFactory *factory, KTextEditor::EditorPrivate::self()->inputModeFactories()) { KateConfigPage *tab = factory->createConfigPage(this); if (tab) { m_inputModeConfigTabs << tab; tabWidget->insertTab(i, tab, tab->name()); connect(tab, SIGNAL(changed()), this, SLOT(slotChanged())); i++; } } layout->addWidget(tabWidget); setLayout(layout); } KateEditConfigTab::~KateEditConfigTab() { qDeleteAll(m_inputModeConfigTabs); } void KateEditConfigTab::apply() { // try to update the rest of tabs editConfigTab->apply(); navigationConfigTab->apply(); indentConfigTab->apply(); completionConfigTab->apply(); spellCheckConfigTab->apply(); Q_FOREACH(KateConfigPage *tab, m_inputModeConfigTabs) { tab->apply(); } } void KateEditConfigTab::reload() { editConfigTab->reload(); navigationConfigTab->reload(); indentConfigTab->reload(); completionConfigTab->reload(); spellCheckConfigTab->reload(); Q_FOREACH(KateConfigPage *tab, m_inputModeConfigTabs) { tab->reload(); } } void KateEditConfigTab::reset() { editConfigTab->reset(); navigationConfigTab->reset(); indentConfigTab->reset(); completionConfigTab->reset(); spellCheckConfigTab->reset(); Q_FOREACH(KateConfigPage *tab, m_inputModeConfigTabs) { tab->reset(); } } void KateEditConfigTab::defaults() { editConfigTab->defaults(); navigationConfigTab->defaults(); indentConfigTab->defaults(); completionConfigTab->defaults(); spellCheckConfigTab->defaults(); Q_FOREACH(KateConfigPage *tab, m_inputModeConfigTabs) { tab->defaults(); } } QString KateEditConfigTab::name() const { return i18n("Editing"); } QString KateEditConfigTab::fullName() const { return i18n("Editing Options"); } QIcon KateEditConfigTab::icon() const { return QIcon::fromTheme(QStringLiteral("accessories-text-editor")); } //END KateEditConfigTab //BEGIN KateViewDefaultsConfig KateViewDefaultsConfig::KateViewDefaultsConfig(QWidget *parent) : KateConfigPage(parent) , textareaUi(new Ui::TextareaAppearanceConfigWidget()) , bordersUi(new Ui::BordersAppearanceConfigWidget()) { QLayout *layout = new QVBoxLayout(this); QTabWidget *tabWidget = new QTabWidget(this); layout->addWidget(tabWidget); layout->setMargin(0); QWidget *textareaTab = new QWidget(tabWidget); textareaUi->setupUi(textareaTab); tabWidget->addTab(textareaTab, i18n("General")); QWidget *bordersTab = new QWidget(tabWidget); bordersUi->setupUi(bordersTab); tabWidget->addTab(bordersTab, i18n("Borders")); textareaUi->cmbDynamicWordWrapIndicator->addItem(i18n("Off")); textareaUi->cmbDynamicWordWrapIndicator->addItem(i18n("Follow Line Numbers")); textareaUi->cmbDynamicWordWrapIndicator->addItem(i18n("Always On")); // What's This? help is in the ui-file reload(); // // after initial reload, connect the stuff for the changed () signal // connect(textareaUi->gbWordWrap, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(textareaUi->cmbDynamicWordWrapIndicator, SIGNAL(activated(int)), this, SLOT(slotChanged())); connect(textareaUi->sbDynamicWordWrapDepth, SIGNAL(valueChanged(int)), this, SLOT(slotChanged())); connect(textareaUi->chkShowTabs, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(textareaUi->chkShowSpaces, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(textareaUi->chkShowIndentationLines, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); + connect(textareaUi->sliSetMarkerSize, SIGNAL(valueChanged(int)), this, SLOT(slotChanged())); connect(textareaUi->chkShowWholeBracketExpression, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(textareaUi->chkAnimateBracketMatching, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(textareaUi->chkFoldFirstLine, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(bordersUi->chkIconBorder, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(bordersUi->chkScrollbarMarks, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(bordersUi->chkScrollbarPreview, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(bordersUi->chkScrollbarMiniMap, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(bordersUi->chkScrollbarMiniMapAll, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); bordersUi->chkScrollbarMiniMapAll->hide(); // this is temporary until the feature is done connect(bordersUi->spBoxMiniMapWidth, SIGNAL(valueChanged(int)), this, SLOT(slotChanged())); connect(bordersUi->chkLineNumbers, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(bordersUi->chkShowLineModification, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(bordersUi->chkShowFoldingMarkers, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(bordersUi->chkShowFoldingPreview, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(bordersUi->rbSortBookmarksByPosition, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(bordersUi->rbSortBookmarksByCreation, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(bordersUi->cmbShowScrollbars, SIGNAL(activated(int)), this, SLOT(slotChanged())); } KateViewDefaultsConfig::~KateViewDefaultsConfig() { delete bordersUi; delete textareaUi; } void KateViewDefaultsConfig::apply() { // nothing changed, no need to apply stuff if (!hasChanged()) { return; } m_changed = false; KateViewConfig::global()->configStart(); KateRendererConfig::global()->configStart(); KateViewConfig::global()->setDynWordWrap(textareaUi->gbWordWrap->isChecked()); KateViewConfig::global()->setDynWordWrapIndicators(textareaUi->cmbDynamicWordWrapIndicator->currentIndex()); KateViewConfig::global()->setDynWordWrapAlignIndent(textareaUi->sbDynamicWordWrapDepth->value()); KateDocumentConfig::global()->setShowTabs(textareaUi->chkShowTabs->isChecked()); KateDocumentConfig::global()->setShowSpaces(textareaUi->chkShowSpaces->isChecked()); + KateDocumentConfig::global()->setMarkerSize(textareaUi->sliSetMarkerSize->value()); KateViewConfig::global()->setLineNumbers(bordersUi->chkLineNumbers->isChecked()); KateViewConfig::global()->setIconBar(bordersUi->chkIconBorder->isChecked()); KateViewConfig::global()->setScrollBarMarks(bordersUi->chkScrollbarMarks->isChecked()); KateViewConfig::global()->setScrollBarPreview(bordersUi->chkScrollbarPreview->isChecked()); KateViewConfig::global()->setScrollBarMiniMap(bordersUi->chkScrollbarMiniMap->isChecked()); KateViewConfig::global()->setScrollBarMiniMapAll(bordersUi->chkScrollbarMiniMapAll->isChecked()); KateViewConfig::global()->setScrollBarMiniMapWidth(bordersUi->spBoxMiniMapWidth->value()); KateViewConfig::global()->setFoldingBar(bordersUi->chkShowFoldingMarkers->isChecked()); KateViewConfig::global()->setFoldingPreview(bordersUi->chkShowFoldingPreview->isChecked()); KateViewConfig::global()->setLineModification(bordersUi->chkShowLineModification->isChecked()); KateViewConfig::global()->setShowScrollbars(bordersUi->cmbShowScrollbars->currentIndex()); KateViewConfig::global()->setBookmarkSort(bordersUi->rbSortBookmarksByPosition->isChecked() ? 0 : 1); KateRendererConfig::global()->setShowIndentationLines(textareaUi->chkShowIndentationLines->isChecked()); KateRendererConfig::global()->setShowWholeBracketExpression(textareaUi->chkShowWholeBracketExpression->isChecked()); KateRendererConfig::global()->setAnimateBracketMatching(textareaUi->chkAnimateBracketMatching->isChecked()); KateViewConfig::global()->setFoldFirstLine(textareaUi->chkFoldFirstLine->isChecked()); KateRendererConfig::global()->configEnd(); KateViewConfig::global()->configEnd(); } void KateViewDefaultsConfig::reload() { textareaUi->gbWordWrap->setChecked(KateViewConfig::global()->dynWordWrap()); textareaUi->cmbDynamicWordWrapIndicator->setCurrentIndex(KateViewConfig::global()->dynWordWrapIndicators()); textareaUi->sbDynamicWordWrapDepth->setValue(KateViewConfig::global()->dynWordWrapAlignIndent()); textareaUi->chkShowTabs->setChecked(KateDocumentConfig::global()->showTabs()); textareaUi->chkShowSpaces->setChecked(KateDocumentConfig::global()->showSpaces()); + textareaUi->sliSetMarkerSize->setValue(KateDocumentConfig::global()->markerSize()); bordersUi->chkLineNumbers->setChecked(KateViewConfig::global()->lineNumbers()); bordersUi->chkIconBorder->setChecked(KateViewConfig::global()->iconBar()); bordersUi->chkScrollbarMarks->setChecked(KateViewConfig::global()->scrollBarMarks()); bordersUi->chkScrollbarPreview->setChecked(KateViewConfig::global()->scrollBarPreview()); bordersUi->chkScrollbarMiniMap->setChecked(KateViewConfig::global()->scrollBarMiniMap()); bordersUi->chkScrollbarMiniMapAll->setChecked(KateViewConfig::global()->scrollBarMiniMapAll()); bordersUi->spBoxMiniMapWidth->setValue(KateViewConfig::global()->scrollBarMiniMapWidth()); bordersUi->chkShowFoldingMarkers->setChecked(KateViewConfig::global()->foldingBar()); bordersUi->chkShowFoldingPreview->setChecked(KateViewConfig::global()->foldingPreview()); bordersUi->chkShowLineModification->setChecked(KateViewConfig::global()->lineModification()); bordersUi->rbSortBookmarksByPosition->setChecked(KateViewConfig::global()->bookmarkSort() == 0); bordersUi->rbSortBookmarksByCreation->setChecked(KateViewConfig::global()->bookmarkSort() == 1); bordersUi->cmbShowScrollbars->setCurrentIndex(KateViewConfig::global()->showScrollbars()); textareaUi->chkShowIndentationLines->setChecked(KateRendererConfig::global()->showIndentationLines()); textareaUi->chkShowWholeBracketExpression->setChecked(KateRendererConfig::global()->showWholeBracketExpression()); textareaUi->chkAnimateBracketMatching->setChecked(KateRendererConfig::global()->animateBracketMatching()); textareaUi->chkFoldFirstLine->setChecked(KateViewConfig::global()->foldFirstLine()); } void KateViewDefaultsConfig::reset() { ; } void KateViewDefaultsConfig::defaults() { ; } QString KateViewDefaultsConfig::name() const { return i18n("Appearance"); } QString KateViewDefaultsConfig::fullName() const { return i18n("Appearance"); } QIcon KateViewDefaultsConfig::icon() const { return QIcon::fromTheme(QStringLiteral("preferences-desktop-theme")); } //END KateViewDefaultsConfig //BEGIN KateSaveConfigTab KateSaveConfigTab::KateSaveConfigTab(QWidget *parent) : KateConfigPage(parent) , modeConfigPage(new ModeConfigPage(this)) { // FIXME: Is really needed to move all this code below to another class, // since it is another tab itself on the config dialog. This means we should // initialize, add and work with as we do with modeConfigPage (ereslibre) QVBoxLayout *layout = new QVBoxLayout; layout->setMargin(0); QTabWidget *tabWidget = new QTabWidget(this); QWidget *tmpWidget = new QWidget(tabWidget); QVBoxLayout *internalLayout = new QVBoxLayout; QWidget *newWidget = new QWidget(tabWidget); ui = new Ui::OpenSaveConfigWidget(); ui->setupUi(newWidget); QWidget *tmpWidget2 = new QWidget(tabWidget); QVBoxLayout *internalLayout2 = new QVBoxLayout; QWidget *newWidget2 = new QWidget(tabWidget); uiadv = new Ui::OpenSaveConfigAdvWidget(); uiadv->setupUi(newWidget2); // What's this help is added in ui/opensaveconfigwidget.ui reload(); // // after initial reload, connect the stuff for the changed () signal // connect(ui->cmbEncoding, SIGNAL(activated(int)), this, SLOT(slotChanged())); connect(ui->cmbEncodingDetection, SIGNAL(activated(int)), this, SLOT(slotChanged())); connect(ui->cmbEncodingFallback, SIGNAL(activated(int)), this, SLOT(slotChanged())); connect(ui->cmbEOL, SIGNAL(activated(int)), this, SLOT(slotChanged())); connect(ui->chkDetectEOL, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(ui->chkEnableBOM, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(ui->lineLengthLimit, SIGNAL(valueChanged(int)), this, SLOT(slotChanged())); connect(ui->cbRemoveTrailingSpaces, SIGNAL(currentIndexChanged(int)), this, SLOT(slotChanged())); connect(ui->chkNewLineAtEof, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(uiadv->chkBackupLocalFiles, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(uiadv->chkBackupRemoteFiles, SIGNAL(toggled(bool)), this, SLOT(slotChanged())); connect(uiadv->edtBackupPrefix, SIGNAL(textChanged(QString)), this, SLOT(slotChanged())); connect(uiadv->edtBackupSuffix, SIGNAL(textChanged(QString)), this, SLOT(slotChanged())); connect(uiadv->cmbSwapFileMode, SIGNAL(currentIndexChanged(int)), this, SLOT(slotChanged())); connect(uiadv->cmbSwapFileMode, SIGNAL(currentIndexChanged(int)), this, SLOT(swapFileModeChanged(int))); connect(uiadv->kurlSwapDirectory, SIGNAL(textChanged(QString)), this, SLOT(slotChanged())); connect(uiadv->spbSwapFileSync, SIGNAL(valueChanged(int)), this, SLOT(slotChanged())); internalLayout->addWidget(newWidget); tmpWidget->setLayout(internalLayout); internalLayout2->addWidget(newWidget2); tmpWidget2->setLayout(internalLayout2); // add all tabs tabWidget->insertTab(0, tmpWidget, i18n("General")); tabWidget->insertTab(1, tmpWidget2, i18n("Advanced")); tabWidget->insertTab(2, modeConfigPage, modeConfigPage->name()); connect(modeConfigPage, SIGNAL(changed()), this, SLOT(slotChanged())); layout->addWidget(tabWidget); setLayout(layout); } KateSaveConfigTab::~KateSaveConfigTab() { delete ui; } void KateSaveConfigTab::swapFileModeChanged(int idx) { const KateDocumentConfig::SwapFileMode mode = static_cast(idx); switch (mode) { case KateDocumentConfig::DisableSwapFile: uiadv->lblSwapDirectory->setEnabled(false); uiadv->kurlSwapDirectory->setEnabled(false); uiadv->lblSwapFileSync->setEnabled(false); uiadv->spbSwapFileSync->setEnabled(false); break; case KateDocumentConfig::EnableSwapFile: uiadv->lblSwapDirectory->setEnabled(false); uiadv->kurlSwapDirectory->setEnabled(false); uiadv->lblSwapFileSync->setEnabled(true); uiadv->spbSwapFileSync->setEnabled(true); break; case KateDocumentConfig::SwapFilePresetDirectory: uiadv->lblSwapDirectory->setEnabled(true); uiadv->kurlSwapDirectory->setEnabled(true); uiadv->lblSwapFileSync->setEnabled(true); uiadv->spbSwapFileSync->setEnabled(true); break; } } void KateSaveConfigTab::apply() { modeConfigPage->apply(); // nothing changed, no need to apply stuff if (!hasChanged()) { return; } m_changed = false; KateGlobalConfig::global()->configStart(); KateDocumentConfig::global()->configStart(); if (uiadv->edtBackupSuffix->text().isEmpty() && uiadv->edtBackupPrefix->text().isEmpty()) { KMessageBox::information( this, i18n("You did not provide a backup suffix or prefix. Using default suffix: '~'"), i18n("No Backup Suffix or Prefix") ); uiadv->edtBackupSuffix->setText(QStringLiteral("~")); } uint f(0); if (uiadv->chkBackupLocalFiles->isChecked()) { f |= KateDocumentConfig::LocalFiles; } if (uiadv->chkBackupRemoteFiles->isChecked()) { f |= KateDocumentConfig::RemoteFiles; } KateDocumentConfig::global()->setBackupFlags(f); KateDocumentConfig::global()->setBackupPrefix(uiadv->edtBackupPrefix->text()); KateDocumentConfig::global()->setBackupSuffix(uiadv->edtBackupSuffix->text()); KateDocumentConfig::global()->setSwapFileMode(uiadv->cmbSwapFileMode->currentIndex()); KateDocumentConfig::global()->setSwapDirectory(uiadv->kurlSwapDirectory->url().toLocalFile()); KateDocumentConfig::global()->setSwapSyncInterval(uiadv->spbSwapFileSync->value()); KateDocumentConfig::global()->setRemoveSpaces(ui->cbRemoveTrailingSpaces->currentIndex()); KateDocumentConfig::global()->setNewLineAtEof(ui->chkNewLineAtEof->isChecked()); // set both standard and fallback encoding KateDocumentConfig::global()->setEncoding(KCharsets::charsets()->encodingForName(ui->cmbEncoding->currentText())); KateGlobalConfig::global()->setProberType((KEncodingProber::ProberType)ui->cmbEncodingDetection->currentIndex()); KateGlobalConfig::global()->setFallbackEncoding(KCharsets::charsets()->encodingForName(ui->cmbEncodingFallback->currentText())); KateDocumentConfig::global()->setEol(ui->cmbEOL->currentIndex()); KateDocumentConfig::global()->setAllowEolDetection(ui->chkDetectEOL->isChecked()); KateDocumentConfig::global()->setBom(ui->chkEnableBOM->isChecked()); KateDocumentConfig::global()->setLineLengthLimit(ui->lineLengthLimit->value()); KateDocumentConfig::global()->configEnd(); KateGlobalConfig::global()->configEnd(); } void KateSaveConfigTab::reload() { modeConfigPage->reload(); // encodings ui->cmbEncoding->clear(); ui->cmbEncodingFallback->clear(); QStringList encodings(KCharsets::charsets()->descriptiveEncodingNames()); int insert = 0; for (int i = 0; i < encodings.count(); i++) { bool found = false; QTextCodec *codecForEnc = KCharsets::charsets()->codecForName(KCharsets::charsets()->encodingForName(encodings[i]), found); if (found) { ui->cmbEncoding->addItem(encodings[i]); ui->cmbEncodingFallback->addItem(encodings[i]); if (codecForEnc == KateDocumentConfig::global()->codec()) { ui->cmbEncoding->setCurrentIndex(insert); } if (codecForEnc == KateGlobalConfig::global()->fallbackCodec()) { // adjust index for fallback config, has no default! ui->cmbEncodingFallback->setCurrentIndex(insert); } insert++; } } // encoding detection ui->cmbEncodingDetection->clear(); bool found = false; for (int i = 0; !KEncodingProber::nameForProberType((KEncodingProber::ProberType) i).isEmpty(); ++i) { ui->cmbEncodingDetection->addItem(KEncodingProber::nameForProberType((KEncodingProber::ProberType) i)); if (i == KateGlobalConfig::global()->proberType()) { ui->cmbEncodingDetection->setCurrentIndex(ui->cmbEncodingDetection->count() - 1); found = true; } } if (!found) { ui->cmbEncodingDetection->setCurrentIndex(KEncodingProber::Universal); } // eol ui->cmbEOL->setCurrentIndex(KateDocumentConfig::global()->eol()); ui->chkDetectEOL->setChecked(KateDocumentConfig::global()->allowEolDetection()); ui->chkEnableBOM->setChecked(KateDocumentConfig::global()->bom()); ui->lineLengthLimit->setValue(KateDocumentConfig::global()->lineLengthLimit()); ui->cbRemoveTrailingSpaces->setCurrentIndex(KateDocumentConfig::global()->removeSpaces()); ui->chkNewLineAtEof->setChecked(KateDocumentConfig::global()->newLineAtEof()); // other stuff uint f(KateDocumentConfig::global()->backupFlags()); uiadv->chkBackupLocalFiles->setChecked(f & KateDocumentConfig::LocalFiles); uiadv->chkBackupRemoteFiles->setChecked(f & KateDocumentConfig::RemoteFiles); uiadv->edtBackupPrefix->setText(KateDocumentConfig::global()->backupPrefix()); uiadv->edtBackupSuffix->setText(KateDocumentConfig::global()->backupSuffix()); uiadv->cmbSwapFileMode->setCurrentIndex(KateDocumentConfig::global()->swapFileModeRaw()); uiadv->kurlSwapDirectory->setUrl(QUrl::fromLocalFile(KateDocumentConfig::global()->swapDirectory())); uiadv->spbSwapFileSync->setValue(KateDocumentConfig::global()->swapSyncInterval()); swapFileModeChanged(KateDocumentConfig::global()->swapFileMode()); } void KateSaveConfigTab::reset() { modeConfigPage->reset(); } void KateSaveConfigTab::defaults() { modeConfigPage->defaults(); ui->cbRemoveTrailingSpaces->setCurrentIndex(0); uiadv->chkBackupLocalFiles->setChecked(true); uiadv->chkBackupRemoteFiles->setChecked(false); uiadv->edtBackupPrefix->setText(QString()); uiadv->edtBackupSuffix->setText(QStringLiteral("~")); uiadv->cmbSwapFileMode->setCurrentIndex(1); uiadv->kurlSwapDirectory->setDisabled(true); uiadv->lblSwapDirectory->setDisabled(true); uiadv->spbSwapFileSync->setValue(15); } QString KateSaveConfigTab::name() const { return i18n("Open/Save"); } QString KateSaveConfigTab::fullName() const { return i18n("File Opening & Saving"); } QIcon KateSaveConfigTab::icon() const { return QIcon::fromTheme(QStringLiteral("document-save")); } //END KateSaveConfigTab //BEGIN KateHlDownloadDialog KateHlDownloadDialog::KateHlDownloadDialog(QWidget *parent, const char *name, bool modal) : QDialog(parent) { setWindowTitle(i18n("Highlight Download")); setObjectName(QString::fromUtf8(name)); setModal(modal); QVBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); QLabel *label = new QLabel(i18n("Select the syntax highlighting files you want to update:"), this); mainLayout->addWidget(label); list = new QTreeWidget(this); list->setColumnCount(4); list->setHeaderLabels(QStringList() << QString() << i18n("Name") << i18n("Installed") << i18n("Latest")); list->setSelectionMode(QAbstractItemView::MultiSelection); list->setAllColumnsShowFocus(true); list->setRootIsDecorated(false); list->setColumnWidth(0, 22); mainLayout->addWidget(list); label = new QLabel(i18n("Note: New versions are selected automatically."), this); mainLayout->addWidget(label); // buttons QDialogButtonBox *buttons = new QDialogButtonBox(this); mainLayout->addWidget(buttons); m_installButton = new QPushButton(QIcon::fromTheme(QStringLiteral("dialog-ok")), i18n("&Install")); m_installButton->setDefault(true); buttons->addButton(m_installButton, QDialogButtonBox::AcceptRole); connect(m_installButton, SIGNAL(clicked()), this, SLOT(slotInstall())); QPushButton *closeButton = new QPushButton; KGuiItem::assign(closeButton, KStandardGuiItem::cancel()); buttons->addButton(closeButton, QDialogButtonBox::RejectRole); connect(closeButton, SIGNAL(clicked()), this, SLOT(reject())); transferJob = KIO::get(QUrl(QStringLiteral("%1update-%2.%3.xml").arg(HLDOWNLOADPATH).arg(KTEXTEDITOR_VERSION_MAJOR).arg(KTEXTEDITOR_VERSION_MINOR)), KIO::Reload); connect(transferJob, SIGNAL(data(KIO::Job*,QByteArray)), this, SLOT(listDataReceived(KIO::Job*,QByteArray))); // void data( KIO::Job *, const QByteArray &data); resize(450, 400); } KateHlDownloadDialog::~KateHlDownloadDialog() {} /// Split typical version string (\c major.minor.patch) into /// numeric components, convert 'em to \c unsigned and form a /// single value that can be compared w/ other versions /// using relation operators. /// \note It takes into account only first 3 numbers unsigned KateHlDownloadDialog::parseVersion(const QString &version_string) { unsigned vn[3] = {0, 0, 0}; unsigned idx = 0; foreach (const QString &n, version_string.split(QLatin1Char('.'))) { vn[idx++] = n.toUInt(); if (idx == sizeof(vn)) { break; } } return (((vn[0]) << 16) | ((vn[1]) << 8) | (vn[2])); } void KateHlDownloadDialog::listDataReceived(KIO::Job *, const QByteArray &data) { if (!transferJob || transferJob->isErrorPage()) { m_installButton->setEnabled(false); if (data.size() == 0) { KMessageBox::error(this, i18n("The list of highlightings could not be found on / retrieved from the server")); } return; } listData += QLatin1String(data); qCDebug(LOG_KTE) << QStringLiteral("CurrentListData: ") << listData; qCDebug(LOG_KTE) << QStringLiteral("Data length: %1").arg(data.size()); qCDebug(LOG_KTE) << QStringLiteral("listData length: %1").arg(listData.length()); if (data.size() == 0) { if (listData.length() > 0) { QString installedVersion; KateHlManager *hlm = KateHlManager::self(); QDomDocument doc; doc.setContent(listData); QDomElement DocElem = doc.documentElement(); QDomNode n = DocElem.firstChild(); KateHighlighting *hl = nullptr; if (n.isNull()) { qCDebug(LOG_KTE) << QStringLiteral("There is no usable childnode"); } while (!n.isNull()) { installedVersion = QStringLiteral(" --"); QDomElement e = n.toElement(); if (!e.isNull()) { qCDebug(LOG_KTE) << QStringLiteral("NAME: ") << e.tagName() << QStringLiteral(" - ") << e.attribute(QStringLiteral("name")); } n = n.nextSibling(); QString Name = e.attribute(QStringLiteral("name")); for (int i = 0; i < hlm->highlights(); i++) { hl = hlm->getHl(i); if (hl && hl->name() == Name) { installedVersion = QLatin1String(" ") + hl->version(); break; } else { hl = nullptr; } } // autoselect entry if new or updated. QTreeWidgetItem *entry = new QTreeWidgetItem(list); entry->setText(0, QString()); entry->setText(1, e.attribute(QStringLiteral("name"))); entry->setText(2, installedVersion); entry->setText(3, e.attribute(QStringLiteral("version"))); entry->setText(4, e.attribute(QStringLiteral("url"))); bool is_fresh = false; if (hl) { unsigned prev_version = parseVersion(hl->version()); unsigned next_version = parseVersion(e.attribute(QStringLiteral("version"))); is_fresh = prev_version < next_version; } else { is_fresh = true; } if (is_fresh) { entry->treeWidget()->setItemSelected(entry, true); entry->setIcon(0, QIcon::fromTheme((QStringLiteral("get-hot-new-stuff")))); } } list->resizeColumnToContents(1); list->sortItems(1, Qt::AscendingOrder); } } } void KateHlDownloadDialog::slotInstall() { const QString destdir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/org.kde.syntax-highlighting/syntax/"); QDir(destdir).mkpath(QStringLiteral(".")); // make sure the dir is there foreach (QTreeWidgetItem *it, list->selectedItems()) { QUrl src(it->text(4)); QString filename = src.fileName(); // if there is no fileName construct at least something if (filename.isEmpty()) { filename = src.path().replace(QLatin1Char('/'), QLatin1Char('_')); } QUrl dest = QUrl::fromLocalFile(destdir + filename); KIO::FileCopyJob *job = KIO::file_copy(src, dest); KJobWidgets::setWindow(job, this); job->exec(); } } //END KateHlDownloadDialog //BEGIN KateGotoBar KateGotoBar::KateGotoBar(KTextEditor::View *view, QWidget *parent) : KateViewBarWidget(true, parent) , m_view(view) { Q_ASSERT(m_view != nullptr); // this bar widget is pointless w/o a view QHBoxLayout *topLayout = new QHBoxLayout(centralWidget()); topLayout->setMargin(0); gotoRange = new QSpinBox(centralWidget()); QLabel *label = new QLabel(i18n("&Go to line:"), centralWidget()); label->setBuddy(gotoRange); QToolButton *btnOK = new QToolButton(centralWidget()); btnOK->setAutoRaise(true); btnOK->setIcon(QIcon::fromTheme(QStringLiteral("go-jump"))); btnOK->setText(i18n("Go")); btnOK->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); connect(btnOK, SIGNAL(clicked()), this, SLOT(gotoLine())); topLayout->addWidget(label); topLayout->addWidget(gotoRange, 1); topLayout->setStretchFactor(gotoRange, 0); topLayout->addWidget(btnOK); topLayout->addStretch(); setFocusProxy(gotoRange); } void KateGotoBar::updateData() { gotoRange->setMaximum(m_view->document()->lines()); if (!isVisible()) { gotoRange->setValue(m_view->cursorPosition().line() + 1); gotoRange->adjustSize(); // ### does not respect the range :-( } gotoRange->setFocus(Qt::OtherFocusReason); gotoRange->selectAll(); } void KateGotoBar::keyPressEvent(QKeyEvent *event) { int key = event->key(); if (key == Qt::Key_Return || key == Qt::Key_Enter) { gotoLine(); return; } KateViewBarWidget::keyPressEvent(event); } void KateGotoBar::gotoLine() { KTextEditor::ViewPrivate *kv = qobject_cast(m_view); if (kv && kv->selection() && !kv->config()->persistentSelection()) { kv->clearSelection(); } m_view->setCursorPosition(KTextEditor::Cursor(gotoRange->value() - 1, 0)); m_view->setFocus(); emit hideMe(); } //END KateGotoBar //BEGIN KateDictionaryBar KateDictionaryBar::KateDictionaryBar(KTextEditor::ViewPrivate *view, QWidget *parent) : KateViewBarWidget(true, parent) , m_view(view) { Q_ASSERT(m_view != nullptr); // this bar widget is pointless w/o a view QHBoxLayout *topLayout = new QHBoxLayout(centralWidget()); topLayout->setMargin(0); //topLayout->setSpacing(spacingHint()); m_dictionaryComboBox = new Sonnet::DictionaryComboBox(centralWidget()); connect(m_dictionaryComboBox, SIGNAL(dictionaryChanged(QString)), this, SLOT(dictionaryChanged(QString))); connect(view->doc(), SIGNAL(defaultDictionaryChanged(KTextEditor::DocumentPrivate*)), this, SLOT(updateData())); QLabel *label = new QLabel(i18n("Dictionary:"), centralWidget()); label->setBuddy(m_dictionaryComboBox); topLayout->addWidget(label); topLayout->addWidget(m_dictionaryComboBox, 1); topLayout->setStretchFactor(m_dictionaryComboBox, 0); topLayout->addStretch(); } KateDictionaryBar::~KateDictionaryBar() { } void KateDictionaryBar::updateData() { KTextEditor::DocumentPrivate *document = m_view->doc(); QString dictionary = document->defaultDictionary(); if (dictionary.isEmpty()) { dictionary = Sonnet::Speller().defaultLanguage(); } m_dictionaryComboBox->setCurrentByDictionary(dictionary); } void KateDictionaryBar::dictionaryChanged(const QString &dictionary) { KTextEditor::Range selection = m_view->selectionRange(); if (selection.isValid() && !selection.isEmpty()) { m_view->doc()->setDictionary(dictionary, selection); } else { m_view->doc()->setDefaultDictionary(dictionary); } } //END KateGotoBar //BEGIN KateModOnHdPrompt KateModOnHdPrompt::KateModOnHdPrompt(KTextEditor::DocumentPrivate *doc, KTextEditor::ModificationInterface::ModifiedOnDiskReason modtype, const QString &reason) : QObject(doc) , m_doc(doc) , m_modtype(modtype) , m_proc(nullptr) , m_diffFile(nullptr) , m_diffAction(nullptr) { m_message = new KTextEditor::Message(reason, KTextEditor::Message::Information); m_message->setPosition(KTextEditor::Message::AboveView); m_message->setWordWrap(true); // If the file isn't deleted, present a diff button const bool onDiskDeleted = modtype == KTextEditor::ModificationInterface::OnDiskDeleted; if (!onDiskDeleted) { if (!QStandardPaths::findExecutable(QStringLiteral("diff")).isEmpty()) { m_diffAction = new QAction(i18n("View &Difference"), this); m_diffAction->setToolTip(i18n("Shows a diff of the changes")); m_message->addAction(m_diffAction, false); connect(m_diffAction, SIGNAL(triggered()), this, SLOT(slotDiff())); } QAction * aReload = new QAction(i18n("&Reload"), this); aReload->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh"))); aReload->setToolTip(i18n("Reload the file from disk. Unsaved changes will be lost.")); m_message->addAction(aReload); connect(aReload, SIGNAL(triggered()), this, SIGNAL(reloadTriggered())); } else { QAction * aSaveAs = new QAction(i18n("&Save As..."), this); aSaveAs->setIcon(QIcon::fromTheme(QStringLiteral("document-save-as"))); aSaveAs->setToolTip(i18n("Lets you select a location and save the file again.")); m_message->addAction(aSaveAs, false); connect(aSaveAs, SIGNAL(triggered()), this, SIGNAL(saveAsTriggered())); } QAction * aIgnore = new QAction(i18n("&Ignore"), this); aIgnore->setToolTip(i18n("Ignores the changes on disk without any action.")); aIgnore->setIcon(KStandardGuiItem::overwrite().icon()); m_message->addAction(aIgnore); connect(aIgnore, SIGNAL(triggered()), this, SIGNAL(ignoreTriggered())); m_doc->postMessage(m_message); } KateModOnHdPrompt::~KateModOnHdPrompt() { delete m_proc; m_proc = nullptr; if (m_diffFile) { m_diffFile->setAutoRemove(true); delete m_diffFile; m_diffFile = nullptr; } delete m_message; } void KateModOnHdPrompt::slotDiff() { if (m_diffFile) { return; } m_diffFile = new QTemporaryFile(); m_diffFile->open(); // Start a KProcess that creates a diff m_proc = new KProcess(this); m_proc->setOutputChannelMode(KProcess::MergedChannels); *m_proc << QStringLiteral("diff") << QLatin1String("-u") << QStringLiteral("-") << m_doc->url().toLocalFile(); connect(m_proc, SIGNAL(readyRead()), this, SLOT(slotDataAvailable())); connect(m_proc, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotPDone())); // disable the diff button, to hinder the user to run it twice. m_diffAction->setEnabled(false); m_proc->start(); QTextStream ts(m_proc); int lastln = m_doc->lines() - 1; for (int l = 0; l < lastln; ++l) { ts << m_doc->line(l) << '\n'; } ts << m_doc->line(lastln); ts.flush(); m_proc->closeWriteChannel(); } void KateModOnHdPrompt::slotDataAvailable() { m_diffFile->write(m_proc->readAll()); } void KateModOnHdPrompt::slotPDone() { m_diffAction->setEnabled(true); const QProcess::ExitStatus es = m_proc->exitStatus(); delete m_proc; m_proc = nullptr; if (es != QProcess::NormalExit) { KMessageBox::sorry(nullptr, i18n("The diff command failed. Please make sure that " "diff(1) is installed and in your PATH."), i18n("Error Creating Diff")); delete m_diffFile; m_diffFile = nullptr; return; } if (m_diffFile->size() == 0) { KMessageBox::information(nullptr, i18n("The files are identical."), i18n("Diff Output")); delete m_diffFile; m_diffFile = nullptr; return; } m_diffFile->setAutoRemove(false); QUrl url = QUrl::fromLocalFile(m_diffFile->fileName()); delete m_diffFile; m_diffFile = nullptr; // KRun::runUrl should delete the file, once the client exits KRun::runUrl(url, QStringLiteral("text/x-patch"), nullptr, KRun::RunFlags(KRun::DeleteTemporaryFiles)); } //END KateModOnHdPrompt diff --git a/src/dialogs/textareaappearanceconfigwidget.ui b/src/dialogs/textareaappearanceconfigwidget.ui index 043f3f01..9fb1e60c 100644 --- a/src/dialogs/textareaappearanceconfigwidget.ui +++ b/src/dialogs/textareaappearanceconfigwidget.ui @@ -1,211 +1,246 @@ - TextareaAppearanceConfigWidget - - - - - - If this option is checked, the text lines will be wrapped at the view border on the screen. - - - &Dynamic Word Wrap - - - true - - - - - - Dynamic &word wrap indicators (if applicable): - - - cmbDynamicWordWrapIndicator - - - - - - - Choose when the Dynamic Word Wrap Indicators should be displayed. - - - - - - - Align dynamically wrapped lines to indentation depth: - - - sbDynamicWordWrapDepth - - - - - - - - 0 - 0 - - - - <p>Enables the start of dynamically wrapped lines to be aligned vertically to the indentation level of the first line. This can help to make code and markup more readable.</p><p>Additionally, this allows you to set a maximum width of the screen, as a percentage, after which dynamically wrapped lines will no longer be vertically aligned. For example, at 50%, lines whose indentation levels are deeper than 50% of the width of the screen will not have vertical alignment applied to subsequent wrapped lines.</p> - - - Disabled - - - % of View Width - - - 80 - - - 10 - - - - - - - - - - Whitespace Highlighting - - - - - - The editor will display a symbol to indicate the presence of a tab in the text. - - - &Highlight tabulators - - - + TextareaAppearanceConfigWidget + + - - - Highlight trailing &spaces - - + + + If this option is checked, the text lines will be wrapped at the view border on the screen. + + + &Dynamic Word Wrap + + + true + + + + + + Dynamic &word wrap indicators (if applicable): + + + cmbDynamicWordWrapIndicator + + + + + + + Choose when the Dynamic Word Wrap Indicators should be displayed. + + + + + + + Align dynamically wrapped lines to indentation depth: + + + sbDynamicWordWrapDepth + + + + + + + + 0 + 0 + + + + <p>Enables the start of dynamically wrapped lines to be aligned vertically to the indentation level of the first line. This can help to make code and markup more readable.</p><p>Additionally, this allows you to set a maximum width of the screen, as a percentage, after which dynamically wrapped lines will no longer be vertically aligned. For example, at 50%, lines whose indentation levels are deeper than 50% of the width of the screen will not have vertical alignment applied to subsequent wrapped lines.</p> + + + Disabled + + + % of View Width + + + 80 + + + 10 + + + + + - - - - - - - Advanced - - - - - If this is enabled, the editor will display vertical lines to help identify indent lines. - - - Show i&ndentation lines - - + + + Whitespace Highlighting + + + + + + The editor will display a symbol to indicate the presence of a tab in the text. + + + &Highlight tabulators + + + + + + + Highlight trailing &spaces + + + + + + + 1 + + + + + + Highlight marker size: + + + + + + + Size of the visible highlight marker. + + + Qt::Horizontal + + + 1 + + + 5 + + + 1 + + + + + + + + - - - If this is enabled, the range between the selected matching brackets will be highlighted. - - - Highlight range between selected brackets - - - - - - - Flash matching brackets - - - If this is enabled, matching brackets are animated for better visibility. - - - Animate bracket matching - - - - - - - When this setting is enabled, the editor view automatically folds + + + Advanced + + + + + + If this is enabled, the editor will display vertical lines to help identify indent lines. + + + Show i&ndentation lines + + + + + + + If this is enabled, the range between the selected matching brackets will be highlighted. + + + Highlight range between selected brackets + + + + + + + Flash matching brackets + + + If this is enabled, matching brackets are animated for better visibility. + + + Animate bracket matching + + + + + + + When this setting is enabled, the editor view automatically folds comment blocks that start on the first line of the document. This is helpful to hide license headers which are commonly placed at the beginning of a file. - - - Fold first line - - + + + Fold first line + + + + + + + + + + Qt::Vertical + + + + 0 + 1 + + + - - - - - - - Qt::Vertical - - - - 0 - 1 - - - - - - - - - KComboBox - QComboBox -
kcombobox.h
-
-
- - - - gbWordWrap - toggled(bool) - cmbDynamicWordWrapIndicator - setEnabled(bool) - - - 115 - 7 - - - 340 - 44 - - - - - gbWordWrap - toggled(bool) - sbDynamicWordWrapDepth - setEnabled(bool) - - - 59 - 6 - - - 385 - 72 - - - - + + + + + KComboBox + QComboBox +
kcombobox.h
+
+
+ + + + gbWordWrap + toggled(bool) + cmbDynamicWordWrapIndicator + setEnabled(bool) + + + 115 + 7 + + + 340 + 44 + + + + + gbWordWrap + toggled(bool) + sbDynamicWordWrapDepth + setEnabled(bool) + + + 59 + 6 + + + 385 + 72 + + + +
diff --git a/src/render/katerenderer.cpp b/src/render/katerenderer.cpp index 62ed5a59..eec7712c 100644 --- a/src/render/katerenderer.cpp +++ b/src/render/katerenderer.cpp @@ -1,1137 +1,1149 @@ /* This file is part of the KDE libraries Copyright (C) 2007 Mirko Stocker Copyright (C) 2003-2005 Hamish Rodda Copyright (C) 2001 Christoph Cullmann Copyright (C) 2001 Joseph Wenninger Copyright (C) 1999 Jochen Wilhelmy Copyright (C) 2013 Andrey Matveyakin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "katerenderer.h" #include "katedocument.h" #include "kateconfig.h" #include "katehighlight.h" #include "kateview.h" #include "katerenderrange.h" #include "katetextlayout.h" #include "katebuffer.h" #include "katepartdebug.h" #include #include #include #include #include #include // qCeil static const QChar tabChar(QLatin1Char('\t')); static const QChar spaceChar(QLatin1Char(' ')); static const QChar nbSpaceChar(0xa0); // non-breaking space KateRenderer::KateRenderer(KTextEditor::DocumentPrivate *doc, Kate::TextFolding &folding, KTextEditor::ViewPrivate *view) : m_doc(doc) , m_folding(folding) , m_view(view) , m_tabWidth(m_doc->config()->tabWidth()) , m_indentWidth(m_doc->config()->indentationWidth()) , m_caretStyle(KateRenderer::Line) , m_drawCaret(true) , m_showSelections(true) , m_showTabs(true) , m_showSpaces(true) , m_showNonPrintableSpaces(false) , m_printerFriendly(false) , m_config(new KateRendererConfig(this)) { updateAttributes(); // initialize with a sane font height updateFontHeight(); + + // make the proper calculation for markerSize + updateMarkerSize(); } KateRenderer::~KateRenderer() { delete m_config; } void KateRenderer::updateAttributes() { m_attributes = m_doc->highlight()->attributes(config()->schema()); } KTextEditor::Attribute::Ptr KateRenderer::attribute(uint pos) const { if (pos < (uint)m_attributes.count()) { return m_attributes[pos]; } return m_attributes[0]; } KTextEditor::Attribute::Ptr KateRenderer::specificAttribute(int context) const { if (context >= 0 && context < m_attributes.count()) { return m_attributes[context]; } return m_attributes[0]; } void KateRenderer::setDrawCaret(bool drawCaret) { m_drawCaret = drawCaret; } void KateRenderer::setCaretStyle(KateRenderer::caretStyles style) { m_caretStyle = style; } void KateRenderer::setShowTabs(bool showTabs) { m_showTabs = showTabs; } void KateRenderer::setShowTrailingSpaces(bool showSpaces) { m_showSpaces = showSpaces; } void KateRenderer::setShowNonPrintableSpaces(const bool on) { m_showNonPrintableSpaces = on; } void KateRenderer::setTabWidth(int tabWidth) { m_tabWidth = tabWidth; } bool KateRenderer::showIndentLines() const { return m_config->showIndentationLines(); } void KateRenderer::setShowIndentLines(bool showIndentLines) { m_config->setShowIndentationLines(showIndentLines); } void KateRenderer::setIndentWidth(int indentWidth) { m_indentWidth = indentWidth; } void KateRenderer::setShowSelections(bool showSelections) { m_showSelections = showSelections; } void KateRenderer::increaseFontSizes(qreal step) { QFont f(config()->font()); f.setPointSizeF(f.pointSizeF() + step); config()->setFont(f); } void KateRenderer::decreaseFontSizes(qreal step) { QFont f(config()->font()); if ((f.pointSizeF() - step) > 0) { f.setPointSizeF(f.pointSizeF() - step); } config()->setFont(f); } bool KateRenderer::isPrinterFriendly() const { return m_printerFriendly; } void KateRenderer::setPrinterFriendly(bool printerFriendly) { m_printerFriendly = printerFriendly; setShowTabs(false); setShowTrailingSpaces(false); setShowSelections(false); setDrawCaret(false); } void KateRenderer::paintTextLineBackground(QPainter &paint, KateLineLayoutPtr layout, int currentViewLine, int xStart, int xEnd) { if (isPrinterFriendly()) { return; } // Normal background color QColor backgroundColor(config()->backgroundColor()); // paint the current line background if we're on the current line QColor currentLineColor = config()->highlightedLineColor(); // Check for mark background int markRed = 0, markGreen = 0, markBlue = 0, markCount = 0; // Retrieve marks for this line uint mrk = m_doc->mark(layout->line()); if (mrk) { for (uint bit = 0; bit < 32; bit++) { KTextEditor::MarkInterface::MarkTypes markType = (KTextEditor::MarkInterface::MarkTypes)(1 << bit); if (mrk & markType) { QColor markColor = config()->lineMarkerColor(markType); if (markColor.isValid()) { markCount++; markRed += markColor.red(); markGreen += markColor.green(); markBlue += markColor.blue(); } } } // for } // Marks if (markCount) { markRed /= markCount; markGreen /= markCount; markBlue /= markCount; backgroundColor.setRgb( int((backgroundColor.red() * 0.9) + (markRed * 0.1)), int((backgroundColor.green() * 0.9) + (markGreen * 0.1)), int((backgroundColor.blue() * 0.9) + (markBlue * 0.1)) ); } // Draw line background paint.fillRect(0, 0, xEnd - xStart, lineHeight() * layout->viewLineCount(), backgroundColor); // paint the current line background if we're on the current line if (currentViewLine != -1) { if (markCount) { markRed /= markCount; markGreen /= markCount; markBlue /= markCount; currentLineColor.setRgb( int((currentLineColor.red() * 0.9) + (markRed * 0.1)), int((currentLineColor.green() * 0.9) + (markGreen * 0.1)), int((currentLineColor.blue() * 0.9) + (markBlue * 0.1)) ); } paint.fillRect(0, lineHeight() * currentViewLine, xEnd - xStart, lineHeight(), currentLineColor); } } void KateRenderer::paintTabstop(QPainter &paint, qreal x, qreal y) { QPen penBackup(paint.pen()); QPen pen(config()->tabMarkerColor()); pen.setWidthF(qMax(1.0, spaceWidth() / 10.0)); paint.setPen(pen); paint.setRenderHint(QPainter::Antialiasing, false); int dist = spaceWidth() * 0.3; QPoint points[8]; points[0] = QPoint(x - dist, y - dist); points[1] = QPoint(x, y); points[2] = QPoint(x, y); points[3] = QPoint(x - dist, y + dist); x += spaceWidth() / 3.0; points[4] = QPoint(x - dist, y - dist); points[5] = QPoint(x, y); points[6] = QPoint(x, y); points[7] = QPoint(x - dist, y + dist); paint.drawLines(points, 4); paint.setPen(penBackup); } void KateRenderer::paintTrailingSpace(QPainter &paint, qreal x, qreal y) { QPen penBackup(paint.pen()); QPen pen(config()->tabMarkerColor()); - pen.setWidthF(spaceWidth() / 3.5); + + pen.setWidthF(m_markerSize); pen.setCapStyle(Qt::RoundCap); paint.setPen(pen); paint.setRenderHint(QPainter::Antialiasing, true); paint.drawPoint(QPointF(x, y)); paint.setPen(penBackup); } void KateRenderer::paintNonBreakSpace(QPainter &paint, qreal x, qreal y) { QPen penBackup(paint.pen()); QPen pen(config()->tabMarkerColor()); pen.setWidthF(qMax(1.0, spaceWidth() / 10.0)); paint.setPen(pen); paint.setRenderHint(QPainter::Antialiasing, false); const int height = fontHeight(); const int width = spaceWidth(); QPoint points[6]; points[0] = QPoint(x + width / 10, y + height / 4); points[1] = QPoint(x + width / 10, y + height / 3); points[2] = QPoint(x + width / 10, y + height / 3); points[3] = QPoint(x + width - width / 10, y + height / 3); points[4] = QPoint(x + width - width / 10, y + height / 3); points[5] = QPoint(x + width - width / 10, y + height / 4); paint.drawLines(points, 3); paint.setPen(penBackup); } void KateRenderer::paintNonPrintableSpaces(QPainter &paint, qreal x, qreal y, const QChar &chr) { paint.save(); QPen pen(config()->spellingMistakeLineColor()); pen.setWidthF(qMax(1.0, spaceWidth() * 0.1)); paint.setPen(pen); paint.setRenderHint(QPainter::Antialiasing, false); const int height = fontHeight(); const int width = config()->fontMetrics().width(chr); const int offset = spaceWidth() * 0.1; QPoint points[8]; points[0] = QPoint(x - offset, y + offset); points[1] = QPoint(x + width + offset, y + offset); points[2] = QPoint(x + width + offset, y + offset); points[3] = QPoint(x + width + offset, y - height - offset); points[4] = QPoint(x + width + offset, y - height - offset); points[5] = QPoint(x - offset, y - height - offset); points[6] = QPoint(x - offset, y - height - offset); points[7] = QPoint(x - offset, y + offset); paint.drawLines(points, 4); paint.restore(); } void KateRenderer::paintIndentMarker(QPainter &paint, uint x, uint y /*row*/) { QPen penBackup(paint.pen()); QPen myPen(config()->indentationLineColor()); static const QVector dashPattern = QVector() << 1 << 1; myPen.setDashPattern(dashPattern); if (y % 2) { myPen.setDashOffset(1); } paint.setPen(myPen); const int height = fontHeight(); const int top = 0; const int bottom = height - 1; QPainter::RenderHints renderHints = paint.renderHints(); paint.setRenderHints(renderHints, false); paint.drawLine(x + 2, top, x + 2, bottom); paint.setRenderHints(renderHints, true); paint.setPen(penBackup); } static bool rangeLessThanForRenderer(const Kate::TextRange *a, const Kate::TextRange *b) { // compare Z-Depth first // smaller Z-Depths should win! if (a->zDepth() > b->zDepth()) { return true; } else if (a->zDepth() < b->zDepth()) { return false; } // end of a > end of b? if (a->end().toCursor() > b->end().toCursor()) { return true; } // if ends are equal, start of a < start of b? if (a->end().toCursor() == b->end().toCursor()) { return a->start().toCursor() < b->start().toCursor(); } return false; } QList KateRenderer::decorationsForLine(const Kate::TextLine &textLine, int line, bool selectionsOnly, KateRenderRange *completionHighlight, bool completionSelected) const { QList newHighlight; // Don't compute the highlighting if there isn't going to be any highlighting QList rangesWithAttributes = m_doc->buffer().rangesForLine(line, m_printerFriendly ? nullptr : m_view, true); if (selectionsOnly || !textLine->attributesList().isEmpty() || !rangesWithAttributes.isEmpty()) { RenderRangeList renderRanges; // Add the inbuilt highlighting to the list NormalRenderRange *inbuiltHighlight = new NormalRenderRange(); const QVector &al = textLine->attributesList(); for (int i = 0; i < al.count(); ++i) if (al[i].length > 0 && al[i].attributeValue > 0) { inbuiltHighlight->addRange(new KTextEditor::Range(KTextEditor::Cursor(line, al[i].offset), al[i].length), specificAttribute(al[i].attributeValue)); } renderRanges.append(inbuiltHighlight); if (!completionHighlight) { // check for dynamic hl stuff const QSet *rangesMouseIn = m_view ? m_view->rangesMouseIn() : nullptr; const QSet *rangesCaretIn = m_view ? m_view->rangesCaretIn() : nullptr; bool anyDynamicHlsActive = m_view && (!rangesMouseIn->empty() || !rangesCaretIn->empty()); // sort all ranges, we want that the most specific ranges win during rendering, multiple equal ranges are kind of random, still better than old smart rangs behavior ;) qSort(rangesWithAttributes.begin(), rangesWithAttributes.end(), rangeLessThanForRenderer); // loop over all ranges for (int i = 0; i < rangesWithAttributes.size(); ++i) { // real range Kate::TextRange *kateRange = rangesWithAttributes[i]; // calculate attribute, default: normal attribute KTextEditor::Attribute::Ptr attribute = kateRange->attribute(); if (anyDynamicHlsActive) { // check mouse in if (KTextEditor::Attribute::Ptr attributeMouseIn = attribute->dynamicAttribute(KTextEditor::Attribute::ActivateMouseIn)) { if (rangesMouseIn->contains(kateRange)) { attribute = attributeMouseIn; } } // check caret in if (KTextEditor::Attribute::Ptr attributeCaretIn = attribute->dynamicAttribute(KTextEditor::Attribute::ActivateCaretIn)) { if (rangesCaretIn->contains(kateRange)) { attribute = attributeCaretIn; } } } // span range NormalRenderRange *additionaHl = new NormalRenderRange(); additionaHl->addRange(new KTextEditor::Range(*kateRange), attribute); renderRanges.append(additionaHl); } } else { // Add the code completion arbitrary highlight to the list renderRanges.append(completionHighlight); } // Add selection highlighting if we're creating the selection decorations if ((m_view && selectionsOnly && showSelections() && m_view->selection()) || (completionHighlight && completionSelected) || (m_view && m_view->blockSelection())) { NormalRenderRange *selectionHighlight = new NormalRenderRange(); // Set up the selection background attribute TODO: move this elsewhere, eg. into the config? static KTextEditor::Attribute::Ptr backgroundAttribute; if (!backgroundAttribute) { backgroundAttribute = KTextEditor::Attribute::Ptr(new KTextEditor::Attribute()); } backgroundAttribute->setBackground(config()->selectionColor()); backgroundAttribute->setForeground(attribute(KTextEditor::dsNormal)->selectedForeground().color()); // Create a range for the current selection if (completionHighlight && completionSelected) { selectionHighlight->addRange(new KTextEditor::Range(line, 0, line + 1, 0), backgroundAttribute); } else if (m_view->blockSelection() && m_view->selectionRange().overlapsLine(line)) { selectionHighlight->addRange(new KTextEditor::Range(m_doc->rangeOnLine(m_view->selectionRange(), line)), backgroundAttribute); } else { selectionHighlight->addRange(new KTextEditor::Range(m_view->selectionRange()), backgroundAttribute); } renderRanges.append(selectionHighlight); // highlighting for the vi visual modes } KTextEditor::Cursor currentPosition, endPosition; // Calculate the range which we need to iterate in order to get the highlighting for just this line if (m_view && selectionsOnly) { if (m_view->blockSelection()) { KTextEditor::Range subRange = m_doc->rangeOnLine(m_view->selectionRange(), line); currentPosition = subRange.start(); endPosition = subRange.end(); } else { KTextEditor::Range rangeNeeded = m_view->selectionRange() & KTextEditor::Range(line, 0, line + 1, 0); currentPosition = qMax(KTextEditor::Cursor(line, 0), rangeNeeded.start()); endPosition = qMin(KTextEditor::Cursor(line + 1, 0), rangeNeeded.end()); } } else { currentPosition = KTextEditor::Cursor(line, 0); endPosition = KTextEditor::Cursor(line + 1, 0); } // Main iterative loop. This walks through each set of highlighting ranges, and stops each // time the highlighting changes. It then creates the corresponding QTextLayout::FormatRanges. while (currentPosition < endPosition) { renderRanges.advanceTo(currentPosition); if (!renderRanges.hasAttribute()) { // No attribute, don't need to create a FormatRange for this text range currentPosition = renderRanges.nextBoundary(); continue; } KTextEditor::Cursor nextPosition = renderRanges.nextBoundary(); // Create the format range and populate with the correct start, length and format info QTextLayout::FormatRange fr; fr.start = currentPosition.column(); if (nextPosition < endPosition || endPosition.line() <= line) { fr.length = nextPosition.column() - currentPosition.column(); } else { // +1 to force background drawing at the end of the line when it's warranted fr.length = textLine->length() - currentPosition.column() + 1; } KTextEditor::Attribute::Ptr a = renderRanges.generateAttribute(); if (a) { fr.format = *a; if (selectionsOnly) { assignSelectionBrushesFromAttribute(fr, *a); } } newHighlight.append(fr); currentPosition = nextPosition; } if (completionHighlight) // Don't delete external completion render range { renderRanges.removeAll(completionHighlight); } qDeleteAll(renderRanges); } return newHighlight; } void KateRenderer::assignSelectionBrushesFromAttribute(QTextLayout::FormatRange &target, const KTextEditor::Attribute &attribute) const { if (attribute.hasProperty(SelectedForeground)) { target.format.setForeground(attribute.selectedForeground()); } if (attribute.hasProperty(SelectedBackground)) { target.format.setBackground(attribute.selectedBackground()); } } /* The ultimate line painting function. Currently missing features: - draw indent lines */ void KateRenderer::paintTextLine(QPainter &paint, KateLineLayoutPtr range, int xStart, int xEnd, const KTextEditor::Cursor *cursor, PaintTextLineFlags flags) { Q_ASSERT(range->isValid()); // qCDebug(LOG_KTE)<<"KateRenderer::paintTextLine"; // font data const QFontMetricsF &fm = config()->fontMetrics(); int currentViewLine = -1; if (cursor && cursor->line() == range->line()) { currentViewLine = range->viewLineForColumn(cursor->column()); } paintTextLineBackground(paint, range, currentViewLine, xStart, xEnd); if (range->layout()) { bool drawSelection = m_view && m_view->selection() && showSelections() && m_view->selectionRange().overlapsLine(range->line()); // Draw selection in block selecton mode. We need 2 kinds of selections that QTextLayout::draw can't render: // - past-end-of-line selection and // - 0-column-wide selection (used to indicate where text will be typed) if (drawSelection && m_view->blockSelection()) { int selectionStartColumn = m_doc->fromVirtualColumn(range->line(), m_doc->toVirtualColumn(m_view->selectionRange().start())); int selectionEndColumn = m_doc->fromVirtualColumn(range->line(), m_doc->toVirtualColumn(m_view->selectionRange().end())); QBrush selectionBrush = config()->selectionColor(); if (selectionStartColumn != selectionEndColumn) { KateTextLayout lastLine = range->viewLine(range->viewLineCount() - 1); if (selectionEndColumn > lastLine.startCol()) { int selectionStartX = (selectionStartColumn > lastLine.startCol()) ? cursorToX(lastLine, selectionStartColumn, true) : 0; int selectionEndX = cursorToX(lastLine, selectionEndColumn, true); paint.fillRect(QRect(selectionStartX - xStart, (int)lastLine.lineLayout().y(), selectionEndX - selectionStartX, lineHeight()), selectionBrush); } } else { const int selectStickWidth = 2; KateTextLayout selectionLine = range->viewLine(range->viewLineForColumn(selectionStartColumn)); int selectionX = cursorToX(selectionLine, selectionStartColumn, true); paint.fillRect(QRect(selectionX - xStart, (int)selectionLine.lineLayout().y(), selectStickWidth, lineHeight()), selectionBrush); } } QVector additionalFormats; if (range->length() > 0) { // We may have changed the pen, be absolutely sure it gets set back to // normal foreground color before drawing text for text that does not // set the pen color paint.setPen(attribute(KTextEditor::dsNormal)->foreground().color()); // Draw the text :) if (drawSelection) { // FIXME toVector() may be a performance issue additionalFormats = decorationsForLine(range->textLine(), range->line(), true).toVector(); range->layout()->draw(&paint, QPoint(-xStart, 0), additionalFormats); } else { range->layout()->draw(&paint, QPoint(-xStart, 0)); } } QBrush backgroundBrush; bool backgroundBrushSet = false; // Loop each individual line for additional text decoration etc. QListIterator it = range->layout()->additionalFormats(); QVectorIterator it2 = additionalFormats; for (int i = 0; i < range->viewLineCount(); ++i) { KateTextLayout line = range->viewLine(i); bool haveBackground = false; // Determine the background to use, if any, for the end of this view line backgroundBrushSet = false; while (it2.hasNext()) { const QTextLayout::FormatRange &fr = it2.peekNext(); if (fr.start > line.endCol()) { break; } if (fr.start + fr.length > line.endCol()) { if (fr.format.hasProperty(QTextFormat::BackgroundBrush)) { backgroundBrushSet = true; backgroundBrush = fr.format.background(); } haveBackground = true; break; } it2.next(); } while (!haveBackground && it.hasNext()) { const QTextLayout::FormatRange &fr = it.peekNext(); if (fr.start > line.endCol()) { break; } if (fr.start + fr.length > line.endCol()) { if (fr.format.hasProperty(QTextFormat::BackgroundBrush)) { backgroundBrushSet = true; backgroundBrush = fr.format.background(); } break; } it.next(); } // Draw selection or background color outside of areas where text is rendered if (!m_printerFriendly) { bool draw = false; QBrush drawBrush; if (m_view && m_view->selection() && !m_view->blockSelection() && m_view->lineEndSelected(line.end(true))) { draw = true; drawBrush = config()->selectionColor(); } else if (backgroundBrushSet && !m_view->blockSelection()) { draw = true; drawBrush = backgroundBrush; } if (draw) { int fillStartX = line.endX() - line.startX() + line.xOffset() - xStart; int fillStartY = lineHeight() * i; int width = xEnd - xStart - fillStartX; int height = lineHeight(); // reverse X for right-aligned lines if (range->layout()->textOption().alignment() == Qt::AlignRight) { fillStartX = 0; } if (width > 0) { QRect area(fillStartX, fillStartY, width, height); paint.fillRect(area, drawBrush); } } } // Draw indent lines if (showIndentLines() && i == 0) { const qreal w = spaceWidth(); const int lastIndentColumn = range->textLine()->indentDepth(m_tabWidth); for (int x = m_indentWidth; x < lastIndentColumn; x += m_indentWidth) { paintIndentMarker(paint, x * w + 1 - xStart, range->line()); } } // draw an open box to mark non-breaking spaces const QString &text = range->textLine()->string(); int y = lineHeight() * i + fm.ascent() - fm.strikeOutPos(); int nbSpaceIndex = text.indexOf(nbSpaceChar, line.lineLayout().xToCursor(xStart)); while (nbSpaceIndex != -1 && nbSpaceIndex < line.endCol()) { int x = line.lineLayout().cursorToX(nbSpaceIndex); if (x > xEnd) { break; } paintNonBreakSpace(paint, x - xStart, y); nbSpaceIndex = text.indexOf(nbSpaceChar, nbSpaceIndex + 1); } // draw tab stop indicators if (showTabs()) { int tabIndex = text.indexOf(tabChar, line.lineLayout().xToCursor(xStart)); while (tabIndex != -1 && tabIndex < line.endCol()) { int x = line.lineLayout().cursorToX(tabIndex); if (x > xEnd) { break; } paintTabstop(paint, x - xStart + spaceWidth() / 2.0, y); tabIndex = text.indexOf(tabChar, tabIndex + 1); } } // draw trailing spaces if (showTrailingSpaces()) { int spaceIndex = line.endCol() - 1; int trailingPos = range->textLine()->lastChar(); if (trailingPos < 0) { trailingPos = 0; } if (spaceIndex >= trailingPos) { while (spaceIndex >= line.startCol() && text.at(spaceIndex).isSpace()) { if (text.at(spaceIndex) != QLatin1Char('\t') || !showTabs()) { if (range->layout()->textOption().alignment() == Qt::AlignRight) { // Draw on left for RTL lines paintTrailingSpace(paint, line.lineLayout().cursorToX(spaceIndex) - xStart - spaceWidth() / 2.0, y); } else { paintTrailingSpace(paint, line.lineLayout().cursorToX(spaceIndex) - xStart + spaceWidth() / 2.0, y); } } --spaceIndex; } } } if (showNonPrintableSpaces()) { const int y = lineHeight() * i + fm.ascent(); static const QRegularExpression nonPrintableSpacesRegExp(QStringLiteral("[\\x{2000}-\\x{200F}\\x{2028}-\\x{202F}\\x{205F}-\\x{2064}\\x{206A}-\\x{206F}]")); QRegularExpressionMatchIterator i = nonPrintableSpacesRegExp.globalMatch(text, line.lineLayout().xToCursor(xStart)); while (i.hasNext()) { const int charIndex = i.next().capturedStart(); const int x = line.lineLayout().cursorToX(charIndex); if (x > xEnd) { break; } paintNonPrintableSpaces(paint, x - xStart, y, text[charIndex]); } } } // draw word-wrap-honor-indent filling if ((range->viewLineCount() > 1) && range->shiftX() && (range->shiftX() > xStart)) { if (backgroundBrushSet) paint.fillRect(0, lineHeight(), range->shiftX() - xStart, lineHeight() * (range->viewLineCount() - 1), backgroundBrush); paint.fillRect(0, lineHeight(), range->shiftX() - xStart, lineHeight() * (range->viewLineCount() - 1), QBrush(config()->wordWrapMarkerColor(), Qt::Dense4Pattern)); } // Draw caret if (drawCaret() && cursor && range->includesCursor(*cursor)) { int caretWidth, lineWidth = 2; QColor color; QTextLine line = range->layout()->lineForTextPosition(qMin(cursor->column(), range->length())); // Determine the caret's style caretStyles style = caretStyle(); // Make the caret the desired width if (style == Line) { caretWidth = lineWidth; } else if (line.isValid() && cursor->column() < range->length()) { caretWidth = int(line.cursorToX(cursor->column() + 1) - line.cursorToX(cursor->column())); if (caretWidth < 0) { caretWidth = -caretWidth; } } else { caretWidth = spaceWidth(); } // Determine the color if (m_caretOverrideColor.isValid()) { // Could actually use the real highlighting system for this... // would be slower, but more accurate for corner cases color = m_caretOverrideColor; } else { // search for the FormatRange that includes the cursor foreach (const QTextLayout::FormatRange &r, range->layout()->additionalFormats()) { if ((r.start <= cursor->column()) && ((r.start + r.length) > cursor->column())) { // check for Qt::NoBrush, as the returned color is black() and no invalid QColor QBrush foregroundBrush = r.format.foreground(); if (foregroundBrush != Qt::NoBrush) { color = r.format.foreground().color(); } break; } } // still no color found, fall back to default style if (!color.isValid()) { color = attribute(KTextEditor::dsNormal)->foreground().color(); } } paint.save(); switch (style) { case Line : paint.setPen(QPen(color, caretWidth)); break; case Block : // use a gray caret so it's possible to see the character color.setAlpha(128); paint.setPen(QPen(color, caretWidth)); break; case Underline : break; case Half : color.setAlpha(128); paint.setPen(QPen(color, caretWidth)); break; } if (cursor->column() <= range->length()) { range->layout()->drawCursor(&paint, QPoint(-xStart, 0), cursor->column(), caretWidth); } else { // Off the end of the line... must be block mode. Draw the caret ourselves. const KateTextLayout &lastLine = range->viewLine(range->viewLineCount() - 1); int x = cursorToX(lastLine, KTextEditor::Cursor(range->line(), cursor->column()), true); if ((x >= xStart) && (x <= xEnd)) { paint.fillRect(x - xStart, (int)lastLine.lineLayout().y(), caretWidth, lineHeight(), color); } } paint.restore(); } } // Draws the dashed underline at the start of a folded block of text. if (!(flags & SkipDrawFirstInvisibleLineUnderlined) && range->startsInvisibleBlock()) { const QPainter::RenderHints backupRenderHints = paint.renderHints(); paint.setRenderHint(QPainter::Antialiasing, false); QPen pen(config()->wordWrapMarkerColor()); pen.setCosmetic(true); pen.setStyle(Qt::DashLine); pen.setDashOffset(xStart); paint.setPen(pen); paint.drawLine(0, (lineHeight() * range->viewLineCount()) - 1, xEnd - xStart, (lineHeight() * range->viewLineCount()) - 1); paint.setRenderHints(backupRenderHints); } // show word wrap marker if desirable if ((!isPrinterFriendly()) && config()->wordWrapMarker() && QFontInfo(config()->font()).fixedPitch()) { const QPainter::RenderHints backupRenderHints = paint.renderHints(); paint.setRenderHint(QPainter::Antialiasing, false); paint.setPen(config()->wordWrapMarkerColor()); int _x = qreal(m_doc->config()->wordWrapAt()) * fm.width(QLatin1Char('x')) - xStart; paint.drawLine(_x, 0, _x, lineHeight()); paint.setRenderHints(backupRenderHints); } } const QFont &KateRenderer::currentFont() const { return config()->font(); } const QFontMetricsF &KateRenderer::currentFontMetrics() const { return config()->fontMetrics(); } uint KateRenderer::fontHeight() const { return m_fontHeight; } uint KateRenderer::documentHeight() const { return m_doc->lines() * lineHeight(); } int KateRenderer::lineHeight() const { return fontHeight(); } bool KateRenderer::getSelectionBounds(int line, int lineLength, int &start, int &end) const { bool hasSel = false; if (m_view->selection() && !m_view->blockSelection()) { if (m_view->lineIsSelection(line)) { start = m_view->selectionRange().start().column(); end = m_view->selectionRange().end().column(); hasSel = true; } else if (line == m_view->selectionRange().start().line()) { start = m_view->selectionRange().start().column(); end = lineLength; hasSel = true; } else if (m_view->selectionRange().containsLine(line)) { start = 0; end = lineLength; hasSel = true; } else if (line == m_view->selectionRange().end().line()) { start = 0; end = m_view->selectionRange().end().column(); hasSel = true; } } else if (m_view->lineHasSelected(line)) { start = m_view->selectionRange().start().column(); end = m_view->selectionRange().end().column(); hasSel = true; } if (start > end) { int temp = end; end = start; start = temp; } return hasSel; } void KateRenderer::updateConfig() { // update the attibute list pointer updateAttributes(); // update font height, do this before we update the view! updateFontHeight(); // trigger view update, if any! if (m_view) { m_view->updateRendererConfig(); } } void KateRenderer::updateFontHeight() { // use height of font + round down, ensure we have at least one pixel // we round down to avoid artifacts: line height too large vs. qt background rendering of text attributes const qreal height = config()->fontMetrics().height(); m_fontHeight = qMax(1, qFloor(height)); } +void KateRenderer::updateMarkerSize() +{ + // marker size will be calculated over the value defined + // on dialog + + m_markerSize = spaceWidth() / (3.5 - (m_doc->config()->markerSize() * 0.5)); +} + qreal KateRenderer::spaceWidth() const { return config()->fontMetrics().width(spaceChar); } void KateRenderer::layoutLine(KateLineLayoutPtr lineLayout, int maxwidth, bool cacheLayout) const { // if maxwidth == -1 we have no wrap Kate::TextLine textLine = lineLayout->textLine(); Q_ASSERT(textLine); QTextLayout *l = lineLayout->layout(); if (!l) { l = new QTextLayout(textLine->string(), config()->font()); } else { l->setText(textLine->string()); l->setFont(config()->font()); } l->setCacheEnabled(cacheLayout); // Initial setup of the QTextLayout. // Tab width QTextOption opt; opt.setFlags(QTextOption::IncludeTrailingSpaces); opt.setTabStop(m_tabWidth * config()->fontMetrics().width(spaceChar)); opt.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); // Find the first strong character in the string. // If it is an RTL character, set the base layout direction of the string to RTL. // // See http://www.unicode.org/reports/tr9/#The_Paragraph_Level (Sections P2 & P3). // Qt's text renderer ("scribe") version 4.2 assumes a "higher-level protocol" // (such as KatePart) will specify the paragraph level, so it does not apply P2 & P3 // by itself. If this ever change in Qt, the next code block could be removed. if (isLineRightToLeft(lineLayout)) { opt.setAlignment(Qt::AlignRight); opt.setTextDirection(Qt::RightToLeft); } else { opt.setAlignment(Qt::AlignLeft); opt.setTextDirection(Qt::LeftToRight); } l->setTextOption(opt); // Syntax highlighting, inbuilt and arbitrary l->setAdditionalFormats(decorationsForLine(textLine, lineLayout->line())); // Begin layouting l->beginLayout(); int height = 0; int shiftX = 0; bool needShiftX = (maxwidth != -1) && m_view && (m_view->config()->dynWordWrapAlignIndent() > 0); forever { QTextLine line = l->createLine(); if (!line.isValid()) { break; } if (maxwidth > 0) { line.setLineWidth(maxwidth); } line.setPosition(QPoint(line.lineNumber() ? shiftX : 0, height)); if (needShiftX && line.width() > 0) { needShiftX = false; // Determine x offset for subsequent-lines-of-paragraph indenting int pos = textLine->nextNonSpaceChar(0); if (pos > 0) { shiftX = (int)line.cursorToX(pos); } // check for too deep shift value and limit if necessary if (shiftX > ((double)maxwidth / 100 * m_view->config()->dynWordWrapAlignIndent())) { shiftX = 0; } // if shiftX > 0, the maxwidth has to adapted maxwidth -= shiftX; lineLayout->setShiftX(shiftX); } height += lineHeight(); } l->endLayout(); lineLayout->setLayout(l); } // 1) QString::isRightToLeft() sux // 2) QString::isRightToLeft() is marked as internal (WTF?) // 3) QString::isRightToLeft() does not seem to work on my setup // 4) isStringRightToLeft() should behave much better than QString::isRightToLeft() therefore: // 5) isStringRightToLeft() kicks ass bool KateRenderer::isLineRightToLeft(KateLineLayoutPtr lineLayout) const { QString s = lineLayout->textLine()->string(); int i = 0; // borrowed from QString::updateProperties() while (i != s.length()) { QChar c = s.at(i); switch (c.direction()) { case QChar::DirL: case QChar::DirLRO: case QChar::DirLRE: return false; case QChar::DirR: case QChar::DirAL: case QChar::DirRLO: case QChar::DirRLE: return true; default: break; } i ++; } return false; #if 0 // or should we use the direction of the widget? QWidget *display = qobject_cast(view()->parent()); if (!display) { return false; } return display->layoutDirection() == Qt::RightToLeft; #endif } int KateRenderer::cursorToX(const KateTextLayout &range, int col, bool returnPastLine) const { return cursorToX(range, KTextEditor::Cursor(range.line(), col), returnPastLine); } int KateRenderer::cursorToX(const KateTextLayout &range, const KTextEditor::Cursor &pos, bool returnPastLine) const { Q_ASSERT(range.isValid()); int x; if (range.lineLayout().width() > 0) { x = (int)range.lineLayout().cursorToX(pos.column()); } else { x = 0; } int over = pos.column() - range.endCol(); if (returnPastLine && over > 0) { x += over * spaceWidth(); } return x; } KTextEditor::Cursor KateRenderer::xToCursor(const KateTextLayout &range, int x, bool returnPastLine) const { Q_ASSERT(range.isValid()); KTextEditor::Cursor ret(range.line(), range.lineLayout().xToCursor(x)); // TODO wrong for RTL lines? if (returnPastLine && range.endCol(true) == -1 && x > range.width() + range.xOffset()) { ret.setColumn(ret.column() + ((x - (range.width() + range.xOffset())) / spaceWidth())); } return ret; } void KateRenderer::setCaretOverrideColor(const QColor &color) { m_caretOverrideColor = color; } diff --git a/src/render/katerenderer.h b/src/render/katerenderer.h index cb7adbdc..15679060 100644 --- a/src/render/katerenderer.h +++ b/src/render/katerenderer.h @@ -1,427 +1,433 @@ /* This file is part of the KDE libraries Copyright (C) 2007 Mirko Stocker Copyright (C) 2003-2005 Hamish Rodda Copyright (C) 2001 Christoph Cullmann Copyright (C) 2001 Joseph Wenninger Copyright (C) 1999 Jochen Wilhelmy This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KATE_RENDERER_H__ #define __KATE_RENDERER_H__ #include #include "katetextline.h" #include "katelinelayout.h" #include #include #include #include #include namespace KTextEditor { class DocumentPrivate; } namespace KTextEditor { class ViewPrivate; } class KateRendererConfig; class KateRenderRange; namespace Kate { class TextFolding; } /** * Handles all of the work of rendering the text * (used for the views and printing) * **/ class KateRenderer { public: /** * Style of Caret * * The caret is displayed as a vertical bar (Line), a filled box * (Block), a horizontal bar (Underline), or a half-height filled * box (Half). The default is Line. * * Line Block Underline Half * * ## _ ######### _ _ * ## __| | #####| |# __| | __| | * ## / _' | ##/ _' |# / _' | / _' | * ##| (_| | #| (#| |# | (_| | #| (#| |# * ## \__,_| ##\__,_|# \__,_| ##\__,_|# * ## ######### ######### ######### */ enum caretStyles { Line, Block, Underline, Half }; /** * Constructor * @param doc document to render * @param folding folding information * @param view view which is output (0 for example for rendering to print) */ explicit KateRenderer(KTextEditor::DocumentPrivate *doc, Kate::TextFolding &folding, KTextEditor::ViewPrivate *view = nullptr); /** * Destructor */ ~KateRenderer(); /** * Returns the document to which this renderer is bound */ KTextEditor::DocumentPrivate *doc() const { return m_doc; } /** * Returns the folding info to which this renderer is bound * @return folding info */ Kate::TextFolding &folding() const { return m_folding; } /** * Returns the view to which this renderer is bound */ KTextEditor::ViewPrivate *view() const { return m_view; } /** * update the highlighting attributes * (for example after an hl change or after hl config changed) */ void updateAttributes(); /** * Determine whether the caret (text cursor) will be drawn. * @return should it be drawn? */ inline bool drawCaret() const { return m_drawCaret; } /** * Set whether the caret (text cursor) will be drawn. * @param drawCaret should caret be drawn? */ void setDrawCaret(bool drawCaret); /** * The style of the caret (text cursor) to be painted. * @return caretStyle */ inline KateRenderer::caretStyles caretStyle() const { return m_caretStyle; } /** * Set the style of caret to be painted. * @param style style to set */ void setCaretStyle(KateRenderer::caretStyles style); /** * Set a \a brush with which to override drawing of the caret. Set to QColor() to clear. */ void setCaretOverrideColor(const QColor &color); /** * @returns whether tabs should be shown (ie. a small mark * drawn to identify a tab) * @return tabs should be shown */ inline bool showTabs() const { return m_showTabs; } /** * Set whether a mark should be painted to help identifying tabs. * @param showTabs show the tabs? */ void setShowTabs(bool showTabs); /** * @returns whether trailing spaces should be shown. */ inline bool showTrailingSpaces() const { return m_showSpaces; } /** * Set whether a mark should be painted for trailing spaces. */ void setShowTrailingSpaces(bool showSpaces); + /** + * Update marker size shown. + */ + void updateMarkerSize(); + /** * @returns whether non-printable spaces should be shown */ inline bool showNonPrintableSpaces() const { return m_showNonPrintableSpaces; } /** * Set whether box should be drawn around non-printable spaces */ void setShowNonPrintableSpaces(const bool showNonPrintableSpaces); /** * Sets the width of the tab. Helps performance. * @param tabWidth new tab width */ void setTabWidth(int tabWidth); /** * @returns whether indent lines should be shown * @return indent lines should be shown */ bool showIndentLines() const; /** * Set whether a guide should be painted to help identifying indent lines. * @param showLines show the indent lines? */ void setShowIndentLines(bool showLines); /** * Sets the width of the tab. Helps performance. * @param indentWidth new indent width */ void setIndentWidth(int indentWidth); /** * Show the view's selection? * @return show sels? */ inline bool showSelections() const { return m_showSelections; } /** * Set whether the view's selections should be shown. * The default is true. * @param showSelections show the selections? */ void setShowSelections(bool showSelections); /** * Change to a different font (soon to be font set?) */ void increaseFontSizes(qreal step = 1.0); void decreaseFontSizes(qreal step = 1.0); const QFont ¤tFont() const; const QFontMetricsF ¤tFontMetrics() const; /** * @return whether the renderer is configured to paint in a * printer-friendly fashion. */ bool isPrinterFriendly() const; /** * Configure this renderer to paint in a printer-friendly fashion. * * Sets the other options appropriately if true. */ void setPrinterFriendly(bool printerFriendly); /** * Text width & height calculation functions... */ void layoutLine(KateLineLayoutPtr line, int maxwidth = -1, bool cacheLayout = false) const; /** * This is a smaller QString::isRightToLeft(). It's also marked as internal to kate * instead of internal to Qt, so we can modify. This method searches for the first * strong character in the paragraph and then returns its direction. In case of a * line without any strong characters, the direction is forced to be LTR. * * Back in KDE 4.1 this method counted chars, which lead to unwanted side effects. * (see https://bugs.kde.org/show_bug.cgi?id=178594). As this function is internal * the way it work will probably change between releases. Be warned! */ bool isLineRightToLeft(KateLineLayoutPtr lineLayout) const; /** * The ultimate decoration creation function. * * \param selectionsOnly return decorations for selections and/or dynamic highlighting. */ QList decorationsForLine(const Kate::TextLine &textLine, int line, bool selectionsOnly = false, KateRenderRange *completionHighlight = nullptr, bool completionSelected = false) const; // Width calculators qreal spaceWidth() const; /** * Returns the x position of cursor \p col on the line \p range. */ int cursorToX(const KateTextLayout &range, int col, bool returnPastLine = false) const; /// \overload int cursorToX(const KateTextLayout &range, const KTextEditor::Cursor &pos, bool returnPastLine = false) const; /** * Returns the real cursor which is occupied by the specified x value, or that closest to it. * If \p returnPastLine is true, the column will be extrapolated out with the assumption * that the extra characters are spaces. */ KTextEditor::Cursor xToCursor(const KateTextLayout &range, int x, bool returnPastLine = false) const; // Font height uint fontHeight() const; // Line height int lineHeight() const; // Document height uint documentHeight() const; // Selection boundaries bool getSelectionBounds(int line, int lineLength, int &start, int &end) const; /** * Flags to customize the paintTextLine function behavior */ enum PaintTextLineFlag { /** * Skip drawing the dashed underline at the start of a folded block of text? */ SkipDrawFirstInvisibleLineUnderlined = 0x1, }; Q_DECLARE_FLAGS(PaintTextLineFlags, PaintTextLineFlag) /** * This is the ultimate function to perform painting of a text line. * * The text line is painted from the upper limit of (0,0). To move that, * apply a transform to your painter. * * @param paint painter to use * @param range layout to use in painting this line * @param xStart starting width in pixels. * @param xEnd ending width in pixels. * @param cursor position of the caret, if placed on the current line. * @param flags flags for customizing the drawing of the line */ void paintTextLine(QPainter &paint, KateLineLayoutPtr range, int xStart, int xEnd, const KTextEditor::Cursor *cursor = nullptr, PaintTextLineFlags flags = PaintTextLineFlags()); /** * Paint the background of a line * * Split off from the main @ref paintTextLine method to make it smaller. As it's being * called only once per line it shouldn't noticably affect performance and it * helps readability a LOT. * * @param paint painter to use * @param layout layout to use in painting this line * @param currentViewLine if one of the view lines is the current line, set * this to the index; otherwise -1. * @param xStart starting width in pixels. * @param xEnd ending width in pixels. */ void paintTextLineBackground(QPainter &paint, KateLineLayoutPtr layout, int currentViewLine, int xStart, int xEnd); /** * This takes an in index, and returns all the attributes for it. * For example, if you have a ktextline, and want the KTextEditor::Attribute * for a given position, do: * * attribute(myktextline->attribute(position)); */ KTextEditor::Attribute::Ptr attribute(uint pos) const; KTextEditor::Attribute::Ptr specificAttribute(int context) const; private: /** * Paint a trailing space on position (x, y). */ void paintTrailingSpace(QPainter &paint, qreal x, qreal y); /** * Paint a tab stop marker on position (x, y). */ void paintTabstop(QPainter &paint, qreal x, qreal y); /** * Paint a non-breaking space marker on position (x, y). */ void paintNonBreakSpace(QPainter &paint, qreal x, qreal y); /** * Paint non printable spaces bounding box */ void paintNonPrintableSpaces(QPainter &paint, qreal x, qreal y, const QChar &chr); /** Paint a SciTE-like indent marker. */ void paintIndentMarker(QPainter &paint, uint x, uint y); void assignSelectionBrushesFromAttribute(QTextLayout::FormatRange &target, const KTextEditor::Attribute &attribute) const; // update font height void updateFontHeight(); KTextEditor::DocumentPrivate *const m_doc; Kate::TextFolding &m_folding; KTextEditor::ViewPrivate *const m_view; // cache of config values int m_tabWidth; int m_indentWidth; int m_fontHeight; // some internal flags KateRenderer::caretStyles m_caretStyle; bool m_drawCaret; bool m_showSelections; bool m_showTabs; bool m_showSpaces; + float m_markerSize; bool m_showNonPrintableSpaces; bool m_printerFriendly; QColor m_caretOverrideColor; QList m_attributes; /** * Configuration */ public: inline KateRendererConfig *config() const { return m_config; } void updateConfig(); private: KateRendererConfig *const m_config; }; #endif diff --git a/src/utils/kateconfig.cpp b/src/utils/kateconfig.cpp index 7f6d3d80..5f983f9a 100644 --- a/src/utils/kateconfig.cpp +++ b/src/utils/kateconfig.cpp @@ -1,3051 +1,3076 @@ /* This file is part of the KDE libraries Copyright (C) 2007, 2008 Matthew Woehlke Copyright (C) 2003 Christoph Cullmann This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kateconfig.h" #include "kateglobal.h" #include "katedefaultcolors.h" #include "katerenderer.h" #include "kateview.h" #include "katedocument.h" #include "kateschema.h" #include "katepartdebug.h" #include #include #include #include #include #include //BEGIN KateConfig KateConfig::KateConfig() : configSessionNumber(0), configIsRunning(false) { } KateConfig::~KateConfig() { } void KateConfig::configStart() { configSessionNumber++; if (configSessionNumber > 1) { return; } configIsRunning = true; } void KateConfig::configEnd() { if (configSessionNumber == 0) { return; } configSessionNumber--; if (configSessionNumber > 0) { return; } configIsRunning = false; updateConfig(); } //END //BEGIN KateDocumentConfig KateGlobalConfig *KateGlobalConfig::s_global = nullptr; KateDocumentConfig *KateDocumentConfig::s_global = nullptr; KateViewConfig *KateViewConfig::s_global = nullptr; KateRendererConfig *KateRendererConfig::s_global = nullptr; KateGlobalConfig::KateGlobalConfig() { s_global = this; // init with defaults from config or really hardcoded ones KConfigGroup cg(KTextEditor::EditorPrivate::config(), "Editor"); readConfig(cg); } KateGlobalConfig::~KateGlobalConfig() { } namespace { const char KEY_PROBER_TYPE[] = "Encoding Prober Type"; const char KEY_FALLBACK_ENCODING[] = "Fallback Encoding"; } void KateGlobalConfig::readConfig(const KConfigGroup &config) { configStart(); setProberType((KEncodingProber::ProberType)config.readEntry(KEY_PROBER_TYPE, (int)KEncodingProber::Universal)); setFallbackEncoding(config.readEntry(KEY_FALLBACK_ENCODING, "")); configEnd(); } void KateGlobalConfig::writeConfig(KConfigGroup &config) { config.writeEntry(KEY_PROBER_TYPE, (int)proberType()); config.writeEntry(KEY_FALLBACK_ENCODING, fallbackEncoding()); } void KateGlobalConfig::updateConfig() { // write config KConfigGroup cg(KTextEditor::EditorPrivate::config(), "Editor"); writeConfig(cg); KTextEditor::EditorPrivate::config()->sync(); } void KateGlobalConfig::setProberType(KEncodingProber::ProberType proberType) { configStart(); m_proberType = proberType; configEnd(); } const QString &KateGlobalConfig::fallbackEncoding() const { return m_fallbackEncoding; } QTextCodec *KateGlobalConfig::fallbackCodec() const { if (m_fallbackEncoding.isEmpty()) { return QTextCodec::codecForName("ISO 8859-15"); } return KCharsets::charsets()->codecForName(m_fallbackEncoding); } bool KateGlobalConfig::setFallbackEncoding(const QString &encoding) { QTextCodec *codec; bool found = false; if (encoding.isEmpty()) { codec = s_global->fallbackCodec(); found = true; } else { codec = KCharsets::charsets()->codecForName(encoding, found); } if (!found || !codec) { return false; } configStart(); m_fallbackEncoding = QString::fromLatin1(codec->name()); configEnd(); return true; } KateDocumentConfig::KateDocumentConfig() : m_indentationWidth(2), m_tabWidth(4), m_tabHandling(tabSmart), m_configFlags(0), m_wordWrapAt(80), m_tabWidthSet(false), m_indentationWidthSet(false), m_indentationModeSet(false), m_wordWrapSet(false), m_wordWrapAtSet(false), m_pageUpDownMovesCursorSet(false), m_keepExtraSpacesSet(false), m_indentPastedTextSet(false), m_backspaceIndentsSet(false), m_smartHomeSet(false), m_showTabsSet(false), m_showSpacesSet(false), m_replaceTabsDynSet(false), m_removeSpacesSet(false), m_newLineAtEofSet(false), m_overwiteModeSet(false), m_tabIndentsSet(false), m_encodingSet(false), m_eolSet(false), m_bomSet(false), m_allowEolDetectionSet(false), m_backupFlagsSet(false), m_backupPrefixSet(false), m_backupSuffixSet(false), m_swapFileModeSet(false), m_swapDirectorySet(false), m_swapSyncIntervalSet(false), m_onTheFlySpellCheckSet(false), m_lineLengthLimitSet(false), m_doc(nullptr) { s_global = this; // init with defaults from config or really hardcoded ones KConfigGroup cg(KTextEditor::EditorPrivate::config(), "Document"); readConfig(cg); } KateDocumentConfig::KateDocumentConfig(const KConfigGroup &cg) : m_indentationWidth(2), m_tabWidth(4), m_tabHandling(tabSmart), m_configFlags(0), m_wordWrapAt(80), m_tabWidthSet(false), m_indentationWidthSet(false), m_indentationModeSet(false), m_wordWrapSet(false), m_wordWrapAtSet(false), m_pageUpDownMovesCursorSet(false), m_keepExtraSpacesSet(false), m_indentPastedTextSet(false), m_backspaceIndentsSet(false), m_smartHomeSet(false), m_showTabsSet(false), m_showSpacesSet(false), m_replaceTabsDynSet(false), m_removeSpacesSet(false), m_newLineAtEofSet(false), m_overwiteModeSet(false), m_tabIndentsSet(false), m_encodingSet(false), m_eolSet(false), m_bomSet(false), m_allowEolDetectionSet(false), m_backupFlagsSet(false), m_backupPrefixSet(false), m_backupSuffixSet(false), m_swapFileModeSet(false), m_swapDirectorySet(false), m_swapSyncIntervalSet(false), m_onTheFlySpellCheckSet(false), m_lineLengthLimitSet(false), m_doc(nullptr) { // init with defaults from config or really hardcoded ones readConfig(cg); } KateDocumentConfig::KateDocumentConfig(KTextEditor::DocumentPrivate *doc) : m_tabHandling(tabSmart), m_configFlags(0), m_tabWidthSet(false), m_indentationWidthSet(false), m_indentationModeSet(false), m_wordWrapSet(false), m_wordWrapAtSet(false), m_pageUpDownMovesCursorSet(false), m_keepExtraSpacesSet(false), m_indentPastedTextSet(false), m_backspaceIndentsSet(false), m_smartHomeSet(false), m_showTabsSet(false), m_showSpacesSet(false), m_replaceTabsDynSet(false), m_removeSpacesSet(false), m_newLineAtEofSet(false), m_overwiteModeSet(false), m_tabIndentsSet(false), m_encodingSet(false), m_eolSet(false), m_bomSet(false), m_allowEolDetectionSet(false), m_backupFlagsSet(false), m_backupPrefixSet(false), m_backupSuffixSet(false), m_swapFileModeSet(false), m_swapDirectorySet(false), m_swapSyncIntervalSet(false), m_onTheFlySpellCheckSet(false), m_lineLengthLimitSet(false), m_doc(doc) { } KateDocumentConfig::~KateDocumentConfig() { } namespace { const char KEY_TAB_WIDTH[] = "Tab Width"; const char KEY_INDENTATION_WIDTH[] = "Indentation Width"; const char KEY_INDENTATION_MODE[] = "Indentation Mode"; const char KEY_TAB_HANDLING[] = "Tab Handling"; const char KEY_WORD_WRAP[] = "Word Wrap"; const char KEY_WORD_WRAP_AT[] = "Word Wrap Column"; const char KEY_PAGEUP_DOWN_MOVES_CURSOR[] = "PageUp/PageDown Moves Cursor"; const char KEY_SMART_HOME[] = "Smart Home"; const char KEY_SHOW_TABS[] = "Show Tabs"; const char KEY_TAB_INDENTS[] = "Indent On Tab"; const char KEY_KEEP_EXTRA_SPACES[] = "Keep Extra Spaces"; const char KEY_INDENT_PASTED_TEXT[] = "Indent On Text Paste"; const char KEY_BACKSPACE_INDENTS[] = "Indent On Backspace"; const char KEY_SHOW_SPACES[] = "Show Spaces"; +const char KEY_MARKER_SIZE[] = "Trailing Marker Size"; const char KEY_REPLACE_TABS_DYN[] = "ReplaceTabsDyn"; const char KEY_REMOVE_SPACES[] = "Remove Spaces"; const char KEY_NEWLINE_AT_EOF[] = "Newline at End of File"; const char KEY_OVR[] = "Overwrite Mode"; const char KEY_ENCODING[] = "Encoding"; const char KEY_EOL[] = "End of Line"; const char KEY_ALLOW_EOL_DETECTION[] = "Allow End of Line Detection"; const char KEY_BOM[] = "BOM"; const char KEY_BACKUP_FLAGS[] = "Backup Flags"; const char KEY_BACKUP_PREFIX[] = "Backup Prefix"; const char KEY_BACKUP_SUFFIX[] = "Backup Suffix"; const char KEY_SWAP_FILE_MODE[] = "Swap File Mode"; const char KEY_SWAP_DIRECTORY[] = "Swap Directory"; const char KEY_SWAP_SYNC_INTERVAL[] = "Swap Sync Interval"; const char KEY_ON_THE_FLY_SPELLCHECK[] = "On-The-Fly Spellcheck"; const char KEY_LINE_LENGTH_LIMIT[] = "Line Length Limit"; } void KateDocumentConfig::readConfig(const KConfigGroup &config) { configStart(); setTabWidth(config.readEntry(KEY_TAB_WIDTH, 4)); setIndentationWidth(config.readEntry(KEY_INDENTATION_WIDTH, 4)); setIndentationMode(config.readEntry(KEY_INDENTATION_MODE, "normal")); setTabHandling(config.readEntry(KEY_TAB_HANDLING, int(KateDocumentConfig::tabSmart))); setWordWrap(config.readEntry(KEY_WORD_WRAP, false)); setWordWrapAt(config.readEntry(KEY_WORD_WRAP_AT, 80)); setPageUpDownMovesCursor(config.readEntry(KEY_PAGEUP_DOWN_MOVES_CURSOR, false)); setSmartHome(config.readEntry(KEY_SMART_HOME, true)); setShowTabs(config.readEntry(KEY_SHOW_TABS, true)); setTabIndents(config.readEntry(KEY_TAB_INDENTS, true)); setKeepExtraSpaces(config.readEntry(KEY_KEEP_EXTRA_SPACES, false)); setIndentPastedText(config.readEntry(KEY_INDENT_PASTED_TEXT, false)); setBackspaceIndents(config.readEntry(KEY_BACKSPACE_INDENTS, true)); setShowSpaces(config.readEntry(KEY_SHOW_SPACES, false)); + setMarkerSize(config.readEntry(KEY_MARKER_SIZE, 1)); setReplaceTabsDyn(config.readEntry(KEY_REPLACE_TABS_DYN, true)); setRemoveSpaces(config.readEntry(KEY_REMOVE_SPACES, 0)); setNewLineAtEof(config.readEntry(KEY_NEWLINE_AT_EOF, true)); setOvr(config.readEntry(KEY_OVR, false)); setEncoding(config.readEntry(KEY_ENCODING, "")); setEol(config.readEntry(KEY_EOL, 0)); setAllowEolDetection(config.readEntry(KEY_ALLOW_EOL_DETECTION, true)); setBom(config.readEntry(KEY_BOM, false)); setBackupFlags(config.readEntry(KEY_BACKUP_FLAGS, 0)); setBackupPrefix(config.readEntry(KEY_BACKUP_PREFIX, QString())); setBackupSuffix(config.readEntry(KEY_BACKUP_SUFFIX, QStringLiteral("~"))); setSwapFileMode(config.readEntry(KEY_SWAP_FILE_MODE, (uint)EnableSwapFile)); setSwapDirectory(config.readEntry(KEY_SWAP_DIRECTORY, QString())); setSwapSyncInterval(config.readEntry(KEY_SWAP_SYNC_INTERVAL, 15)); setOnTheFlySpellCheck(config.readEntry(KEY_ON_THE_FLY_SPELLCHECK, false)); setLineLengthLimit(config.readEntry(KEY_LINE_LENGTH_LIMIT, 4096)); configEnd(); } void KateDocumentConfig::writeConfig(KConfigGroup &config) { config.writeEntry(KEY_TAB_WIDTH, tabWidth()); config.writeEntry(KEY_INDENTATION_WIDTH, indentationWidth()); config.writeEntry(KEY_INDENTATION_MODE, indentationMode()); config.writeEntry(KEY_TAB_HANDLING, tabHandling()); config.writeEntry(KEY_WORD_WRAP, wordWrap()); config.writeEntry(KEY_WORD_WRAP_AT, wordWrapAt()); config.writeEntry(KEY_PAGEUP_DOWN_MOVES_CURSOR, pageUpDownMovesCursor()); config.writeEntry(KEY_SMART_HOME, smartHome()); config.writeEntry(KEY_SHOW_TABS, showTabs()); config.writeEntry(KEY_TAB_INDENTS, tabIndentsEnabled()); config.writeEntry(KEY_KEEP_EXTRA_SPACES, keepExtraSpaces()); config.writeEntry(KEY_INDENT_PASTED_TEXT, indentPastedText()); config.writeEntry(KEY_BACKSPACE_INDENTS, backspaceIndents()); config.writeEntry(KEY_SHOW_SPACES, showSpaces()); + config.writeEntry(KEY_MARKER_SIZE, markerSize()); config.writeEntry(KEY_REPLACE_TABS_DYN, replaceTabsDyn()); config.writeEntry(KEY_REMOVE_SPACES, removeSpaces()); config.writeEntry(KEY_NEWLINE_AT_EOF, newLineAtEof()); config.writeEntry(KEY_OVR, ovr()); config.writeEntry(KEY_ENCODING, encoding()); config.writeEntry(KEY_EOL, eol()); config.writeEntry(KEY_ALLOW_EOL_DETECTION, allowEolDetection()); config.writeEntry(KEY_BOM, bom()); config.writeEntry(KEY_BACKUP_FLAGS, backupFlags()); config.writeEntry(KEY_BACKUP_PREFIX, backupPrefix()); config.writeEntry(KEY_BACKUP_SUFFIX, backupSuffix()); config.writeEntry(KEY_SWAP_FILE_MODE, swapFileModeRaw()); config.writeEntry(KEY_SWAP_DIRECTORY, swapDirectory()); config.writeEntry(KEY_SWAP_SYNC_INTERVAL, swapSyncInterval()); config.writeEntry(KEY_ON_THE_FLY_SPELLCHECK, onTheFlySpellCheck()); config.writeEntry(KEY_LINE_LENGTH_LIMIT, lineLengthLimit()); } void KateDocumentConfig::updateConfig() { if (m_doc) { m_doc->updateConfig(); return; } if (isGlobal()) { for (int z = 0; z < KTextEditor::EditorPrivate::self()->kateDocuments().size(); ++z) { (KTextEditor::EditorPrivate::self()->kateDocuments())[z]->updateConfig(); } // write config KConfigGroup cg(KTextEditor::EditorPrivate::config(), "Document"); writeConfig(cg); KTextEditor::EditorPrivate::config()->sync(); } } int KateDocumentConfig::tabWidth() const { if (m_tabWidthSet || isGlobal()) { return m_tabWidth; } return s_global->tabWidth(); } void KateDocumentConfig::setTabWidth(int tabWidth) { if (tabWidth < 1) { return; } if (m_tabWidthSet && m_tabWidth == tabWidth) { return; } configStart(); m_tabWidthSet = true; m_tabWidth = tabWidth; configEnd(); } int KateDocumentConfig::indentationWidth() const { if (m_indentationWidthSet || isGlobal()) { return m_indentationWidth; } return s_global->indentationWidth(); } void KateDocumentConfig::setIndentationWidth(int indentationWidth) { if (indentationWidth < 1) { return; } if (m_indentationWidthSet && m_indentationWidth == indentationWidth) { return; } configStart(); m_indentationWidthSet = true; m_indentationWidth = indentationWidth; configEnd(); } const QString &KateDocumentConfig::indentationMode() const { if (m_indentationModeSet || isGlobal()) { return m_indentationMode; } return s_global->indentationMode(); } void KateDocumentConfig::setIndentationMode(const QString &indentationMode) { if (m_indentationModeSet && m_indentationMode == indentationMode) { return; } configStart(); m_indentationModeSet = true; m_indentationMode = indentationMode; configEnd(); } uint KateDocumentConfig::tabHandling() const { // This setting is purly a user preference, // hence, there exists only the global setting. if (isGlobal()) { return m_tabHandling; } return s_global->tabHandling(); } void KateDocumentConfig::setTabHandling(uint tabHandling) { configStart(); m_tabHandling = tabHandling; configEnd(); } bool KateDocumentConfig::wordWrap() const { if (m_wordWrapSet || isGlobal()) { return m_wordWrap; } return s_global->wordWrap(); } void KateDocumentConfig::setWordWrap(bool on) { if (m_wordWrapSet && m_wordWrap == on) { return; } configStart(); m_wordWrapSet = true; m_wordWrap = on; configEnd(); } int KateDocumentConfig::wordWrapAt() const { if (m_wordWrapAtSet || isGlobal()) { return m_wordWrapAt; } return s_global->wordWrapAt(); } void KateDocumentConfig::setWordWrapAt(int col) { if (col < 1) { return; } if (m_wordWrapAtSet && m_wordWrapAt == col) { return; } configStart(); m_wordWrapAtSet = true; m_wordWrapAt = col; configEnd(); } bool KateDocumentConfig::pageUpDownMovesCursor() const { if (m_pageUpDownMovesCursorSet || isGlobal()) { return m_pageUpDownMovesCursor; } return s_global->pageUpDownMovesCursor(); } void KateDocumentConfig::setPageUpDownMovesCursor(bool on) { if (m_pageUpDownMovesCursorSet && m_pageUpDownMovesCursor == on) { return; } configStart(); m_pageUpDownMovesCursorSet = true; m_pageUpDownMovesCursor = on; configEnd(); } void KateDocumentConfig::setKeepExtraSpaces(bool on) { if (m_keepExtraSpacesSet && m_keepExtraSpaces == on) { return; } configStart(); m_keepExtraSpacesSet = true; m_keepExtraSpaces = on; configEnd(); } bool KateDocumentConfig::keepExtraSpaces() const { if (m_keepExtraSpacesSet || isGlobal()) { return m_keepExtraSpaces; } return s_global->keepExtraSpaces(); } void KateDocumentConfig::setIndentPastedText(bool on) { if (m_indentPastedTextSet && m_indentPastedText == on) { return; } configStart(); m_indentPastedTextSet = true; m_indentPastedText = on; configEnd(); } bool KateDocumentConfig::indentPastedText() const { if (m_indentPastedTextSet || isGlobal()) { return m_indentPastedText; } return s_global->indentPastedText(); } void KateDocumentConfig::setBackspaceIndents(bool on) { if (m_backspaceIndentsSet && m_backspaceIndents == on) { return; } configStart(); m_backspaceIndentsSet = true; m_backspaceIndents = on; configEnd(); } bool KateDocumentConfig::backspaceIndents() const { if (m_backspaceIndentsSet || isGlobal()) { return m_backspaceIndents; } return s_global->backspaceIndents(); } void KateDocumentConfig::setSmartHome(bool on) { if (m_smartHomeSet && m_smartHome == on) { return; } configStart(); m_smartHomeSet = true; m_smartHome = on; configEnd(); } bool KateDocumentConfig::smartHome() const { if (m_smartHomeSet || isGlobal()) { return m_smartHome; } return s_global->smartHome(); } void KateDocumentConfig::setShowTabs(bool on) { if (m_showTabsSet && m_showTabs == on) { return; } configStart(); m_showTabsSet = true; m_showTabs = on; configEnd(); } bool KateDocumentConfig::showTabs() const { if (m_showTabsSet || isGlobal()) { return m_showTabs; } return s_global->showTabs(); } void KateDocumentConfig::setShowSpaces(bool on) { if (m_showSpacesSet && m_showSpaces == on) { return; } configStart(); m_showSpacesSet = true; m_showSpaces = on; configEnd(); } bool KateDocumentConfig::showSpaces() const { if (m_showSpacesSet || isGlobal()) { return m_showSpaces; } return s_global->showSpaces(); } +void KateDocumentConfig::setMarkerSize(uint markerSize) +{ + if (m_markerSize == markerSize) { + return; + } + + configStart(); + + m_markerSize = markerSize; + + configEnd(); +} + +uint KateDocumentConfig::markerSize() const +{ + if (isGlobal()) { + return m_markerSize; + } + + return s_global->markerSize(); +} + void KateDocumentConfig::setReplaceTabsDyn(bool on) { if (m_replaceTabsDynSet && m_replaceTabsDyn == on) { return; } configStart(); m_replaceTabsDynSet = true; m_replaceTabsDyn = on; configEnd(); } bool KateDocumentConfig::replaceTabsDyn() const { if (m_replaceTabsDynSet || isGlobal()) { return m_replaceTabsDyn; } return s_global->replaceTabsDyn(); } void KateDocumentConfig::setRemoveSpaces(int triState) { if (m_removeSpacesSet && m_removeSpaces == triState) { return; } configStart(); m_removeSpacesSet = true; m_removeSpaces = triState; configEnd(); } int KateDocumentConfig::removeSpaces() const { if (m_removeSpacesSet || isGlobal()) { return m_removeSpaces; } return s_global->removeSpaces(); } void KateDocumentConfig::setNewLineAtEof(bool on) { if (m_newLineAtEofSet && m_newLineAtEof == on) { return; } configStart(); m_newLineAtEofSet = true; m_newLineAtEof = on; configEnd(); } bool KateDocumentConfig::newLineAtEof() const { if (m_newLineAtEofSet || isGlobal()) { return m_newLineAtEof; } return s_global->newLineAtEof(); } void KateDocumentConfig::setOvr(bool on) { if (m_overwiteModeSet && m_overwiteMode == on) { return; } configStart(); m_overwiteModeSet = true; m_overwiteMode = on; configEnd(); } bool KateDocumentConfig::ovr() const { if (m_overwiteModeSet || isGlobal()) { return m_overwiteMode; } return s_global->ovr(); } void KateDocumentConfig::setTabIndents(bool on) { if (m_tabIndentsSet && m_tabIndents == on) { return; } configStart(); m_tabIndentsSet = true; m_tabIndents = on; configEnd(); } bool KateDocumentConfig::tabIndentsEnabled() const { if (m_tabIndentsSet || isGlobal()) { return m_tabIndents; } return s_global->tabIndentsEnabled(); } const QString &KateDocumentConfig::encoding() const { if (m_encodingSet || isGlobal()) { return m_encoding; } return s_global->encoding(); } QTextCodec *KateDocumentConfig::codec() const { if (m_encodingSet || isGlobal()) { if (m_encoding.isEmpty() && isGlobal()) { // default to UTF-8, this makes sense to have a usable encoding detection // else for people that have by bad luck some encoding like latin1 as default, no encoding detection will work // see e.g. bug 362604 for windows return QTextCodec::codecForName("UTF-8"); } else if (m_encoding.isEmpty()) { return s_global->codec(); } else { return KCharsets::charsets()->codecForName(m_encoding); } } return s_global->codec(); } bool KateDocumentConfig::setEncoding(const QString &encoding) { QTextCodec *codec; bool found = false; if (encoding.isEmpty()) { codec = s_global->codec(); found = true; } else { codec = KCharsets::charsets()->codecForName(encoding, found); } if (!found || !codec) { return false; } configStart(); m_encodingSet = true; m_encoding = QString::fromLatin1(codec->name()); configEnd(); return true; } bool KateDocumentConfig::isSetEncoding() const { return m_encodingSet; } int KateDocumentConfig::eol() const { if (m_eolSet || isGlobal()) { return m_eol; } return s_global->eol(); } QString KateDocumentConfig::eolString() { if (eol() == KateDocumentConfig::eolUnix) { return QStringLiteral("\n"); } else if (eol() == KateDocumentConfig::eolDos) { return QStringLiteral("\r\n"); } else if (eol() == KateDocumentConfig::eolMac) { return QStringLiteral("\r"); } return QStringLiteral("\n"); } void KateDocumentConfig::setEol(int mode) { if (m_eolSet && m_eol == mode) { return; } configStart(); m_eolSet = true; m_eol = mode; configEnd(); } void KateDocumentConfig::setBom(bool bom) { if (m_bomSet && m_bom == bom) { return; } configStart(); m_bomSet = true; m_bom = bom; configEnd(); } bool KateDocumentConfig::bom() const { if (m_bomSet || isGlobal()) { return m_bom; } return s_global->bom(); } bool KateDocumentConfig::allowEolDetection() const { if (m_allowEolDetectionSet || isGlobal()) { return m_allowEolDetection; } return s_global->allowEolDetection(); } void KateDocumentConfig::setAllowEolDetection(bool on) { if (m_allowEolDetectionSet && m_allowEolDetection == on) { return; } configStart(); m_allowEolDetectionSet = true; m_allowEolDetection = on; configEnd(); } uint KateDocumentConfig::backupFlags() const { if (m_backupFlagsSet || isGlobal()) { return m_backupFlags; } return s_global->backupFlags(); } void KateDocumentConfig::setBackupFlags(uint flags) { if (m_backupFlagsSet && m_backupFlags == flags) { return; } configStart(); m_backupFlagsSet = true; m_backupFlags = flags; configEnd(); } const QString &KateDocumentConfig::backupPrefix() const { if (m_backupPrefixSet || isGlobal()) { return m_backupPrefix; } return s_global->backupPrefix(); } const QString &KateDocumentConfig::backupSuffix() const { if (m_backupSuffixSet || isGlobal()) { return m_backupSuffix; } return s_global->backupSuffix(); } void KateDocumentConfig::setBackupPrefix(const QString &prefix) { if (m_backupPrefixSet && m_backupPrefix == prefix) { return; } configStart(); m_backupPrefixSet = true; m_backupPrefix = prefix; configEnd(); } void KateDocumentConfig::setBackupSuffix(const QString &suffix) { if (m_backupSuffixSet && m_backupSuffix == suffix) { return; } configStart(); m_backupSuffixSet = true; m_backupSuffix = suffix; configEnd(); } uint KateDocumentConfig::swapSyncInterval() const { if (m_swapSyncIntervalSet || isGlobal()) { return m_swapSyncInterval; } return s_global->swapSyncInterval(); } void KateDocumentConfig::setSwapSyncInterval(uint interval) { if (m_swapSyncIntervalSet && m_swapSyncInterval == interval) { return; } configStart(); m_swapSyncIntervalSet = true; m_swapSyncInterval = interval; configEnd(); } uint KateDocumentConfig::swapFileModeRaw() const { if (m_swapFileModeSet || isGlobal()) { return m_swapFileMode; } return s_global->swapFileModeRaw(); } KateDocumentConfig::SwapFileMode KateDocumentConfig::swapFileMode() const { return static_cast(swapFileModeRaw()); } void KateDocumentConfig::setSwapFileMode(uint mode) { if (m_swapFileModeSet && m_swapFileMode == mode) { return; } configStart(); m_swapFileModeSet = true; m_swapFileMode = mode; configEnd(); } const QString &KateDocumentConfig::swapDirectory() const { if (m_swapDirectorySet || isGlobal()) { return m_swapDirectory; } return s_global->swapDirectory(); } void KateDocumentConfig::setSwapDirectory(const QString &directory) { if (m_swapDirectorySet && m_swapDirectory == directory) { return; } configStart(); m_swapDirectorySet = true; m_swapDirectory = directory; configEnd(); } bool KateDocumentConfig::onTheFlySpellCheck() const { if (isGlobal()) { // WARNING: this is slightly hackish, but it's currently the only way to // do it, see also the KTextEdit class QSettings settings(QStringLiteral("KDE"), QStringLiteral("Sonnet")); return settings.value(QStringLiteral("checkerEnabledByDefault"), false).toBool(); //KConfigGroup configGroup(KSharedConfig::openConfig(), "Spelling"); //return configGroup.readEntry("checkerEnabledByDefault", false); } if (m_onTheFlySpellCheckSet) { return m_onTheFlySpellCheck; } return s_global->onTheFlySpellCheck(); } void KateDocumentConfig::setOnTheFlySpellCheck(bool on) { if (m_onTheFlySpellCheckSet && m_onTheFlySpellCheck == on) { return; } configStart(); m_onTheFlySpellCheckSet = true; m_onTheFlySpellCheck = on; configEnd(); } int KateDocumentConfig::lineLengthLimit() const { if (m_lineLengthLimitSet || isGlobal()) { return m_lineLengthLimit; } return s_global->lineLengthLimit(); } void KateDocumentConfig::setLineLengthLimit(int lineLengthLimit) { if (m_lineLengthLimitSet && m_lineLengthLimit == lineLengthLimit) { return; } configStart(); m_lineLengthLimitSet = true; m_lineLengthLimit = lineLengthLimit; configEnd(); } //END //BEGIN KateViewConfig KateViewConfig::KateViewConfig() : m_showWordCount(false), m_dynWordWrapSet(false), m_dynWordWrapIndicatorsSet(false), m_dynWordWrapAlignIndentSet(false), m_lineNumbersSet(false), m_scrollBarMarksSet(false), m_scrollBarPreviewSet(false), m_scrollBarMiniMapSet(false), m_scrollBarMiniMapAllSet(false), m_scrollBarMiniMapWidthSet(false), m_showScrollbarsSet(false), m_iconBarSet(false), m_foldingBarSet(false), m_foldingPreviewSet(false), m_lineModificationSet(false), m_bookmarkSortSet(false), m_autoCenterLinesSet(false), m_searchFlagsSet(false), m_defaultMarkTypeSet(false), m_persistentSelectionSet(false), m_inputModeSet(false), m_viInputModeStealKeysSet(false), m_viRelativeLineNumbersSet(false), m_automaticCompletionInvocationSet(false), m_wordCompletionSet(false), m_keywordCompletionSet(false), m_wordCompletionMinimalWordLengthSet(false), m_smartCopyCutSet(false), m_scrollPastEndSet(false), m_allowMarkMenu(true), m_wordCompletionRemoveTailSet(false), m_foldFirstLineSet (false), m_autoBracketsSet(false), m_view(nullptr) { s_global = this; // init with defaults from config or really hardcoded ones KConfigGroup config(KTextEditor::EditorPrivate::config(), "View"); readConfig(config); } KateViewConfig::KateViewConfig(KTextEditor::ViewPrivate *view) : m_searchFlags(PowerModePlainText), m_maxHistorySize(100), m_showWordCount(false), m_dynWordWrapSet(false), m_dynWordWrapIndicatorsSet(false), m_dynWordWrapAlignIndentSet(false), m_lineNumbersSet(false), m_scrollBarMarksSet(false), m_scrollBarPreviewSet(false), m_scrollBarMiniMapSet(false), m_scrollBarMiniMapAllSet(false), m_scrollBarMiniMapWidthSet(false), m_showScrollbarsSet(false), m_iconBarSet(false), m_foldingBarSet(false), m_foldingPreviewSet(false), m_lineModificationSet(false), m_bookmarkSortSet(false), m_autoCenterLinesSet(false), m_searchFlagsSet(false), m_defaultMarkTypeSet(false), m_persistentSelectionSet(false), m_inputModeSet(false), m_viInputModeStealKeysSet(false), m_viRelativeLineNumbersSet(false), m_automaticCompletionInvocationSet(false), m_wordCompletionSet(false), m_keywordCompletionSet(false), m_wordCompletionMinimalWordLengthSet(false), m_smartCopyCutSet(false), m_scrollPastEndSet(false), m_allowMarkMenu(true), m_wordCompletionRemoveTailSet(false), m_foldFirstLineSet (false), m_autoBracketsSet(false), m_view(view) { } KateViewConfig::~KateViewConfig() { } namespace { const char KEY_SEARCH_REPLACE_FLAGS[] = "Search/Replace Flags"; const char KEY_DYN_WORD_WRAP[] = "Dynamic Word Wrap"; const char KEY_DYN_WORD_WRAP_INDICATORS[] = "Dynamic Word Wrap Indicators"; const char KEY_DYN_WORD_WRAP_ALIGN_INDENT[] = "Dynamic Word Wrap Align Indent"; const char KEY_LINE_NUMBERS[] = "Line Numbers"; const char KEY_SCROLL_BAR_MARKS[] = "Scroll Bar Marks"; const char KEY_SCROLL_BAR_PREVIEW[] = "Scroll Bar Preview"; const char KEY_SCROLL_BAR_MINI_MAP[] = "Scroll Bar MiniMap"; const char KEY_SCROLL_BAR_MINI_MAP_ALL[] = "Scroll Bar Mini Map All"; const char KEY_SCROLL_BAR_MINI_MAP_WIDTH[] = "Scroll Bar Mini Map Width"; const char KEY_SHOW_SCROLLBARS[] = "Show Scrollbars"; const char KEY_ICON_BAR[] = "Icon Bar"; const char KEY_FOLDING_BAR[] = "Folding Bar"; const char KEY_FOLDING_PREVIEW[] = "Folding Preview"; const char KEY_LINE_MODIFICATION[] = "Line Modification"; const char KEY_BOOKMARK_SORT[] = "Bookmark Menu Sorting"; const char KEY_AUTO_CENTER_LINES[] = "Auto Center Lines"; const char KEY_MAX_HISTORY_SIZE[] = "Maximum Search History Size"; const char KEY_DEFAULT_MARK_TYPE[] = "Default Mark Type"; const char KEY_ALLOW_MARK_MENU[] = "Allow Mark Menu"; const char KEY_PERSISTENT_SELECTION[] = "Persistent Selection"; const char KEY_INPUT_MODE[] = "Input Mode"; const char KEY_VI_INPUT_MODE_STEAL_KEYS[] = "Vi Input Mode Steal Keys"; const char KEY_VI_RELATIVE_LINE_NUMBERS[] = "Vi Relative Line Numbers"; const char KEY_AUTOMATIC_COMPLETION_INVOCATION[] = "Auto Completion"; const char KEY_WORD_COMPLETION[] = "Word Completion"; const char KEY_KEYWORD_COMPLETION[] = "Keyword Completion"; const char KEY_WORD_COMPLETION_MINIMAL_WORD_LENGTH[] = "Word Completion Minimal Word Length"; const char KEY_WORD_COMPLETION_REMOVE_TAIL[] = "Word Completion Remove Tail"; const char KEY_SMART_COPY_CUT[] = "Smart Copy Cut"; const char KEY_SCROLL_PAST_END[] = "Scroll Past End"; const char KEY_FOLD_FIRST_LINE[] = "Fold First Line"; const char KEY_SHOW_WORD_COUNT[] = "Show Word Count"; const char KEY_AUTO_BRACKETS[] = "Auto Brackets"; } void KateViewConfig::readConfig(const KConfigGroup &config) { configStart(); // default on setDynWordWrap(config.readEntry(KEY_DYN_WORD_WRAP, true)); setDynWordWrapIndicators(config.readEntry(KEY_DYN_WORD_WRAP_INDICATORS, 1)); setDynWordWrapAlignIndent(config.readEntry(KEY_DYN_WORD_WRAP_ALIGN_INDENT, 80)); setLineNumbers(config.readEntry(KEY_LINE_NUMBERS, false)); setScrollBarMarks(config.readEntry(KEY_SCROLL_BAR_MARKS, false)); setScrollBarPreview(config.readEntry(KEY_SCROLL_BAR_PREVIEW, true)); setScrollBarMiniMap(config.readEntry(KEY_SCROLL_BAR_MINI_MAP, true)); setScrollBarMiniMapAll(config.readEntry(KEY_SCROLL_BAR_MINI_MAP_ALL, false)); setScrollBarMiniMapWidth(config.readEntry(KEY_SCROLL_BAR_MINI_MAP_WIDTH, 60)); setShowScrollbars(config.readEntry(KEY_SHOW_SCROLLBARS, static_cast(AlwaysOn))); setIconBar(config.readEntry(KEY_ICON_BAR, false)); setFoldingBar(config.readEntry(KEY_FOLDING_BAR, true)); setFoldingPreview(config.readEntry(KEY_FOLDING_PREVIEW, true)); setLineModification(config.readEntry(KEY_LINE_MODIFICATION, false)); setBookmarkSort(config.readEntry(KEY_BOOKMARK_SORT, 0)); setAutoCenterLines(config.readEntry(KEY_AUTO_CENTER_LINES, 0)); setSearchFlags(config.readEntry(KEY_SEARCH_REPLACE_FLAGS, IncFromCursor | PowerMatchCase | PowerModePlainText)); m_maxHistorySize = config.readEntry(KEY_MAX_HISTORY_SIZE, 100); setDefaultMarkType(config.readEntry(KEY_DEFAULT_MARK_TYPE, int(KTextEditor::MarkInterface::markType01))); setAllowMarkMenu(config.readEntry(KEY_ALLOW_MARK_MENU, true)); setPersistentSelection(config.readEntry(KEY_PERSISTENT_SELECTION, false)); setInputModeRaw(config.readEntry(KEY_INPUT_MODE, 0)); setViInputModeStealKeys(config.readEntry(KEY_VI_INPUT_MODE_STEAL_KEYS, false)); setViRelativeLineNumbers(config.readEntry(KEY_VI_RELATIVE_LINE_NUMBERS, false)); setAutomaticCompletionInvocation(config.readEntry(KEY_AUTOMATIC_COMPLETION_INVOCATION, true)); setWordCompletion(config.readEntry(KEY_WORD_COMPLETION, true)); setKeywordCompletion(config.readEntry(KEY_KEYWORD_COMPLETION, true)); setWordCompletionMinimalWordLength(config.readEntry(KEY_WORD_COMPLETION_MINIMAL_WORD_LENGTH, 3)); setWordCompletionRemoveTail(config.readEntry(KEY_WORD_COMPLETION_REMOVE_TAIL, true)); setSmartCopyCut(config.readEntry(KEY_SMART_COPY_CUT, false)); setScrollPastEnd(config.readEntry(KEY_SCROLL_PAST_END, false)); setFoldFirstLine(config.readEntry(KEY_FOLD_FIRST_LINE, false)); setShowWordCount(config.readEntry(KEY_SHOW_WORD_COUNT, false)); setAutoBrackets(config.readEntry(KEY_AUTO_BRACKETS, false)); configEnd(); } void KateViewConfig::writeConfig(KConfigGroup &config) { config.writeEntry(KEY_DYN_WORD_WRAP, dynWordWrap()); config.writeEntry(KEY_DYN_WORD_WRAP_INDICATORS, dynWordWrapIndicators()); config.writeEntry(KEY_DYN_WORD_WRAP_ALIGN_INDENT, dynWordWrapAlignIndent()); config.writeEntry(KEY_LINE_NUMBERS, lineNumbers()); config.writeEntry(KEY_SCROLL_BAR_MARKS, scrollBarMarks()); config.writeEntry(KEY_SCROLL_BAR_PREVIEW, scrollBarPreview()); config.writeEntry(KEY_SCROLL_BAR_MINI_MAP, scrollBarMiniMap()); config.writeEntry(KEY_SCROLL_BAR_MINI_MAP_ALL, scrollBarMiniMapAll()); config.writeEntry(KEY_SCROLL_BAR_MINI_MAP_WIDTH, scrollBarMiniMapWidth()); config.writeEntry(KEY_SHOW_SCROLLBARS, showScrollbars()); config.writeEntry(KEY_ICON_BAR, iconBar()); config.writeEntry(KEY_FOLDING_BAR, foldingBar()); config.writeEntry(KEY_FOLDING_PREVIEW, foldingPreview()); config.writeEntry(KEY_LINE_MODIFICATION, lineModification()); config.writeEntry(KEY_BOOKMARK_SORT, bookmarkSort()); config.writeEntry(KEY_AUTO_CENTER_LINES, autoCenterLines()); config.writeEntry(KEY_SEARCH_REPLACE_FLAGS, int(searchFlags())); config.writeEntry(KEY_MAX_HISTORY_SIZE, m_maxHistorySize); config.writeEntry(KEY_DEFAULT_MARK_TYPE, defaultMarkType()); config.writeEntry(KEY_ALLOW_MARK_MENU, allowMarkMenu()); config.writeEntry(KEY_PERSISTENT_SELECTION, persistentSelection()); config.writeEntry(KEY_AUTOMATIC_COMPLETION_INVOCATION, automaticCompletionInvocation()); config.writeEntry(KEY_WORD_COMPLETION, wordCompletion()); config.writeEntry(KEY_KEYWORD_COMPLETION, keywordCompletion()); config.writeEntry(KEY_WORD_COMPLETION_MINIMAL_WORD_LENGTH, wordCompletionMinimalWordLength()); config.writeEntry(KEY_WORD_COMPLETION_REMOVE_TAIL, wordCompletionRemoveTail()); config.writeEntry(KEY_SMART_COPY_CUT, smartCopyCut()); config.writeEntry(KEY_SCROLL_PAST_END, scrollPastEnd()); config.writeEntry(KEY_FOLD_FIRST_LINE, foldFirstLine()); config.writeEntry(KEY_INPUT_MODE, static_cast(inputMode())); config.writeEntry(KEY_VI_INPUT_MODE_STEAL_KEYS, viInputModeStealKeys()); config.writeEntry(KEY_VI_RELATIVE_LINE_NUMBERS, viRelativeLineNumbers()); config.writeEntry(KEY_SHOW_WORD_COUNT, showWordCount()); config.writeEntry(KEY_AUTO_BRACKETS, autoBrackets()); } void KateViewConfig::updateConfig() { if (m_view) { m_view->updateConfig(); return; } if (isGlobal()) { foreach (KTextEditor::ViewPrivate* view, KTextEditor::EditorPrivate::self()->views()) { view->updateConfig(); } // write config KConfigGroup cg(KTextEditor::EditorPrivate::config(), "View"); writeConfig(cg); KTextEditor::EditorPrivate::config()->sync(); } } bool KateViewConfig::dynWordWrap() const { if (m_dynWordWrapSet || isGlobal()) { return m_dynWordWrap; } return s_global->dynWordWrap(); } void KateViewConfig::setDynWordWrap(bool wrap) { if (m_dynWordWrapSet && m_dynWordWrap == wrap) { return; } configStart(); m_dynWordWrapSet = true; m_dynWordWrap = wrap; configEnd(); } int KateViewConfig::dynWordWrapIndicators() const { if (m_dynWordWrapIndicatorsSet || isGlobal()) { return m_dynWordWrapIndicators; } return s_global->dynWordWrapIndicators(); } void KateViewConfig::setDynWordWrapIndicators(int mode) { if (m_dynWordWrapIndicatorsSet && m_dynWordWrapIndicators == mode) { return; } configStart(); m_dynWordWrapIndicatorsSet = true; m_dynWordWrapIndicators = qBound(0, mode, 80); configEnd(); } int KateViewConfig::dynWordWrapAlignIndent() const { if (m_dynWordWrapAlignIndentSet || isGlobal()) { return m_dynWordWrapAlignIndent; } return s_global->dynWordWrapAlignIndent(); } void KateViewConfig::setDynWordWrapAlignIndent(int indent) { if (m_dynWordWrapAlignIndentSet && m_dynWordWrapAlignIndent == indent) { return; } configStart(); m_dynWordWrapAlignIndentSet = true; m_dynWordWrapAlignIndent = indent; configEnd(); } bool KateViewConfig::lineNumbers() const { if (m_lineNumbersSet || isGlobal()) { return m_lineNumbers; } return s_global->lineNumbers(); } void KateViewConfig::setLineNumbers(bool on) { if (m_lineNumbersSet && m_lineNumbers == on) { return; } configStart(); m_lineNumbersSet = true; m_lineNumbers = on; configEnd(); } bool KateViewConfig::scrollBarMarks() const { if (m_scrollBarMarksSet || isGlobal()) { return m_scrollBarMarks; } return s_global->scrollBarMarks(); } void KateViewConfig::setScrollBarMarks(bool on) { if (m_scrollBarMarksSet && m_scrollBarMarks == on) { return; } configStart(); m_scrollBarMarksSet = true; m_scrollBarMarks = on; configEnd(); } bool KateViewConfig::scrollBarPreview() const { if (m_scrollBarPreviewSet || isGlobal()) { return m_scrollBarPreview; } return s_global->scrollBarPreview(); } void KateViewConfig::setScrollBarPreview(bool on) { if (m_scrollBarPreviewSet && m_scrollBarPreview == on) { return; } configStart(); m_scrollBarPreviewSet = true; m_scrollBarPreview = on; configEnd(); } bool KateViewConfig::scrollBarMiniMap() const { if (m_scrollBarMiniMapSet || isGlobal()) { return m_scrollBarMiniMap; } return s_global->scrollBarMiniMap(); } void KateViewConfig::setScrollBarMiniMap(bool on) { if (m_scrollBarMiniMapSet && m_scrollBarMiniMap == on) { return; } configStart(); m_scrollBarMiniMapSet = true; m_scrollBarMiniMap = on; configEnd(); } bool KateViewConfig::scrollBarMiniMapAll() const { if (m_scrollBarMiniMapAllSet || isGlobal()) { return m_scrollBarMiniMapAll; } return s_global->scrollBarMiniMapAll(); } void KateViewConfig::setScrollBarMiniMapAll(bool on) { if (m_scrollBarMiniMapAllSet && m_scrollBarMiniMapAll == on) { return; } configStart(); m_scrollBarMiniMapAllSet = true; m_scrollBarMiniMapAll = on; configEnd(); } int KateViewConfig::scrollBarMiniMapWidth() const { if (m_scrollBarMiniMapWidthSet || isGlobal()) { return m_scrollBarMiniMapWidth; } return s_global->scrollBarMiniMapWidth(); } void KateViewConfig::setScrollBarMiniMapWidth(int width) { if (m_scrollBarMiniMapWidthSet && m_scrollBarMiniMapWidth == width) { return; } configStart(); m_scrollBarMiniMapWidthSet = true; m_scrollBarMiniMapWidth = width; configEnd(); } int KateViewConfig::showScrollbars() const { if (m_showScrollbarsSet || isGlobal()) { return m_showScrollbars; } return s_global->showScrollbars(); } void KateViewConfig::setShowScrollbars(int mode) { if (m_showScrollbarsSet && m_showScrollbars == mode) { return; } configStart(); m_showScrollbarsSet = true; m_showScrollbars = qBound(0, mode, 80); configEnd(); } bool KateViewConfig::autoBrackets() const { if (m_autoBracketsSet || isGlobal()) { return m_autoBrackets; } return s_global->autoBrackets(); } void KateViewConfig::setAutoBrackets(bool on) { if (m_autoBracketsSet && m_autoBrackets == on) { return; } configStart(); m_autoBracketsSet = true; m_autoBrackets = on; configEnd(); } bool KateViewConfig::iconBar() const { if (m_iconBarSet || isGlobal()) { return m_iconBar; } return s_global->iconBar(); } void KateViewConfig::setIconBar(bool on) { if (m_iconBarSet && m_iconBar == on) { return; } configStart(); m_iconBarSet = true; m_iconBar = on; configEnd(); } bool KateViewConfig::foldingBar() const { if (m_foldingBarSet || isGlobal()) { return m_foldingBar; } return s_global->foldingBar(); } void KateViewConfig::setFoldingBar(bool on) { if (m_foldingBarSet && m_foldingBar == on) { return; } configStart(); m_foldingBarSet = true; m_foldingBar = on; configEnd(); } bool KateViewConfig::foldingPreview() const { if (m_foldingPreviewSet || isGlobal()) { return m_foldingPreview; } return s_global->foldingPreview(); } void KateViewConfig::setFoldingPreview(bool on) { if (m_foldingPreviewSet && m_foldingPreview == on) { return; } configStart(); m_foldingPreviewSet = true; m_foldingPreview = on; configEnd(); } bool KateViewConfig::lineModification() const { if (m_lineModificationSet || isGlobal()) { return m_lineModification; } return s_global->lineModification(); } void KateViewConfig::setLineModification(bool on) { if (m_lineModificationSet && m_lineModification == on) { return; } configStart(); m_lineModificationSet = true; m_lineModification = on; configEnd(); } int KateViewConfig::bookmarkSort() const { if (m_bookmarkSortSet || isGlobal()) { return m_bookmarkSort; } return s_global->bookmarkSort(); } void KateViewConfig::setBookmarkSort(int mode) { if (m_bookmarkSortSet && m_bookmarkSort == mode) { return; } configStart(); m_bookmarkSortSet = true; m_bookmarkSort = mode; configEnd(); } int KateViewConfig::autoCenterLines() const { if (m_autoCenterLinesSet || isGlobal()) { return m_autoCenterLines; } return s_global->autoCenterLines(); } void KateViewConfig::setAutoCenterLines(int lines) { if (lines < 0) { return; } if (m_autoCenterLinesSet && m_autoCenterLines == lines) { return; } configStart(); m_autoCenterLinesSet = true; m_autoCenterLines = lines; configEnd(); } long KateViewConfig::searchFlags() const { if (m_searchFlagsSet || isGlobal()) { return m_searchFlags; } return s_global->searchFlags(); } void KateViewConfig::setSearchFlags(long flags) { if (m_searchFlagsSet && m_searchFlags == flags) { return; } configStart(); m_searchFlagsSet = true; m_searchFlags = flags; configEnd(); } int KateViewConfig::maxHistorySize() const { return m_maxHistorySize; } uint KateViewConfig::defaultMarkType() const { if (m_defaultMarkTypeSet || isGlobal()) { return m_defaultMarkType; } return s_global->defaultMarkType(); } void KateViewConfig::setDefaultMarkType(uint type) { if (m_defaultMarkTypeSet && m_defaultMarkType == type) { return; } configStart(); m_defaultMarkTypeSet = true; m_defaultMarkType = type; configEnd(); } bool KateViewConfig::allowMarkMenu() const { return m_allowMarkMenu; } void KateViewConfig::setAllowMarkMenu(bool allow) { m_allowMarkMenu = allow; } bool KateViewConfig::persistentSelection() const { if (m_persistentSelectionSet || isGlobal()) { return m_persistentSelection; } return s_global->persistentSelection(); } void KateViewConfig::setPersistentSelection(bool on) { if (m_persistentSelectionSet && m_persistentSelection == on) { return; } configStart(); m_persistentSelectionSet = true; m_persistentSelection = on; configEnd(); } KTextEditor::View::InputMode KateViewConfig::inputMode() const { if (m_inputModeSet || isGlobal()) { return m_inputMode; } return s_global->inputMode(); } void KateViewConfig::setInputMode(KTextEditor::View::InputMode mode) { if (m_inputModeSet && m_inputMode == mode) { return; } configStart(); m_inputModeSet = true; m_inputMode = mode; configEnd(); } void KateViewConfig::setInputModeRaw(int rawmode) { setInputMode(static_cast(rawmode)); } bool KateViewConfig::viInputModeStealKeys() const { if (m_viInputModeStealKeysSet || isGlobal()) { return m_viInputModeStealKeys; } return s_global->viInputModeStealKeys(); } void KateViewConfig::setViInputModeStealKeys(bool on) { if (m_viInputModeStealKeysSet && m_viInputModeStealKeys == on) { return; } configStart(); m_viInputModeStealKeysSet = true; m_viInputModeStealKeys = on; configEnd(); } bool KateViewConfig::viRelativeLineNumbers() const { if (m_viRelativeLineNumbersSet || isGlobal()) { return m_viRelativeLineNumbers; } return s_global->viRelativeLineNumbers(); } void KateViewConfig::setViRelativeLineNumbers(bool on) { if (m_viRelativeLineNumbersSet && m_viRelativeLineNumbers == on) { return; } configStart(); m_viRelativeLineNumbersSet = true; m_viRelativeLineNumbers = on; configEnd(); } bool KateViewConfig::automaticCompletionInvocation() const { if (m_automaticCompletionInvocationSet || isGlobal()) { return m_automaticCompletionInvocation; } return s_global->automaticCompletionInvocation(); } void KateViewConfig::setAutomaticCompletionInvocation(bool on) { if (m_automaticCompletionInvocationSet && m_automaticCompletionInvocation == on) { return; } configStart(); m_automaticCompletionInvocationSet = true; m_automaticCompletionInvocation = on; configEnd(); } bool KateViewConfig::wordCompletion() const { if (m_wordCompletionSet || isGlobal()) { return m_wordCompletion; } return s_global->wordCompletion(); } void KateViewConfig::setWordCompletion(bool on) { if (m_wordCompletionSet && m_wordCompletion == on) { return; } configStart(); m_wordCompletionSet = true; m_wordCompletion = on; configEnd(); } bool KateViewConfig::keywordCompletion() const { if (m_keywordCompletionSet || isGlobal()) return m_keywordCompletion; return s_global->keywordCompletion(); } void KateViewConfig::setKeywordCompletion(bool on) { if (m_keywordCompletionSet && m_keywordCompletion == on) return; configStart(); m_keywordCompletionSet = true; m_keywordCompletion = on; configEnd(); } int KateViewConfig::wordCompletionMinimalWordLength() const { if (m_wordCompletionMinimalWordLengthSet || isGlobal()) { return m_wordCompletionMinimalWordLength; } return s_global->wordCompletionMinimalWordLength(); } void KateViewConfig::setWordCompletionMinimalWordLength(int length) { if (m_wordCompletionMinimalWordLengthSet && m_wordCompletionMinimalWordLength == length) { return; } configStart(); m_wordCompletionMinimalWordLengthSet = true; m_wordCompletionMinimalWordLength = length; configEnd(); } bool KateViewConfig::wordCompletionRemoveTail() const { if (m_wordCompletionRemoveTailSet || isGlobal()) { return m_wordCompletionRemoveTail; } return s_global->wordCompletionRemoveTail(); } void KateViewConfig::setWordCompletionRemoveTail(bool on) { if (m_wordCompletionRemoveTailSet && m_wordCompletionRemoveTail == on) { return; } configStart(); m_wordCompletionRemoveTailSet = true; m_wordCompletionRemoveTail = on; configEnd(); } bool KateViewConfig::smartCopyCut() const { if (m_smartCopyCutSet || isGlobal()) { return m_smartCopyCut; } return s_global->smartCopyCut(); } void KateViewConfig::setSmartCopyCut(bool on) { if (m_smartCopyCutSet && m_smartCopyCut == on) { return; } configStart(); m_smartCopyCutSet = true; m_smartCopyCut = on; configEnd(); } bool KateViewConfig::scrollPastEnd() const { if (m_scrollPastEndSet || isGlobal()) { return m_scrollPastEnd; } return s_global->scrollPastEnd(); } void KateViewConfig::setScrollPastEnd(bool on) { if (m_scrollPastEndSet && m_scrollPastEnd == on) { return; } configStart(); m_scrollPastEndSet = true; m_scrollPastEnd = on; configEnd(); } bool KateViewConfig::foldFirstLine() const { if (m_foldFirstLineSet || isGlobal()) { return m_foldFirstLine; } return s_global->foldFirstLine(); } void KateViewConfig::setFoldFirstLine(bool on) { if (m_foldFirstLineSet && m_foldFirstLine == on) { return; } configStart(); m_foldFirstLineSet = true; m_foldFirstLine = on; configEnd(); } bool KateViewConfig::showWordCount() { return m_showWordCount; } void KateViewConfig::setShowWordCount(bool on) { if (m_showWordCount == on) { return; } configStart(); m_showWordCount = on; configEnd(); } //END //BEGIN KateRendererConfig KateRendererConfig::KateRendererConfig() : m_fontMetrics(QFont()), m_lineMarkerColor(KTextEditor::MarkInterface::reservedMarkersCount()), m_wordWrapMarker(false), m_showIndentationLines(false), m_showWholeBracketExpression(false), m_animateBracketMatching(false), m_schemaSet(false), m_fontSet(false), m_wordWrapMarkerSet(false), m_showIndentationLinesSet(false), m_showWholeBracketExpressionSet(false), m_backgroundColorSet(false), m_selectionColorSet(false), m_highlightedLineColorSet(false), m_highlightedBracketColorSet(false), m_wordWrapMarkerColorSet(false), m_tabMarkerColorSet(false), m_indentationLineColorSet(false), m_iconBarColorSet(false), m_foldingColorSet(false), m_lineNumberColorSet(false), m_currentLineNumberColorSet(false), m_separatorColorSet(false), m_spellingMistakeLineColorSet(false), m_templateColorsSet(false), m_modifiedLineColorSet(false), m_savedLineColorSet(false), m_searchHighlightColorSet(false), m_replaceHighlightColorSet(false), m_lineMarkerColorSet(m_lineMarkerColor.size()), m_renderer(nullptr) { // init bitarray m_lineMarkerColorSet.fill(true); s_global = this; // init with defaults from config or really hardcoded ones KConfigGroup config(KTextEditor::EditorPrivate::config(), "Renderer"); readConfig(config); } KateRendererConfig::KateRendererConfig(KateRenderer *renderer) : m_fontMetrics(QFont()), m_lineMarkerColor(KTextEditor::MarkInterface::reservedMarkersCount()), m_schemaSet(false), m_fontSet(false), m_wordWrapMarkerSet(false), m_showIndentationLinesSet(false), m_showWholeBracketExpressionSet(false), m_backgroundColorSet(false), m_selectionColorSet(false), m_highlightedLineColorSet(false), m_highlightedBracketColorSet(false), m_wordWrapMarkerColorSet(false), m_tabMarkerColorSet(false), m_indentationLineColorSet(false), m_iconBarColorSet(false), m_foldingColorSet(false), m_lineNumberColorSet(false), m_currentLineNumberColorSet(false), m_separatorColorSet(false), m_spellingMistakeLineColorSet(false), m_templateColorsSet(false), m_modifiedLineColorSet(false), m_savedLineColorSet(false), m_searchHighlightColorSet(false), m_replaceHighlightColorSet(false), m_lineMarkerColorSet(m_lineMarkerColor.size()), m_renderer(renderer) { // init bitarray m_lineMarkerColorSet.fill(false); } KateRendererConfig::~KateRendererConfig() { } namespace { const char KEY_SCHEMA[] = "Schema"; const char KEY_WORD_WRAP_MARKER[] = "Word Wrap Marker"; const char KEY_SHOW_INDENTATION_LINES[] = "Show Indentation Lines"; const char KEY_SHOW_WHOLE_BRACKET_EXPRESSION[] = "Show Whole Bracket Expression"; const char KEY_ANIMATE_BRACKET_MATCHING[] = "Animate Bracket Matching"; } void KateRendererConfig::readConfig(const KConfigGroup &config) { configStart(); // "Normal" Schema MUST BE THERE, see global kateschemarc setSchema(config.readEntry(KEY_SCHEMA, "Normal")); setWordWrapMarker(config.readEntry(KEY_WORD_WRAP_MARKER, false)); setShowIndentationLines(config.readEntry(KEY_SHOW_INDENTATION_LINES, false)); setShowWholeBracketExpression(config.readEntry(KEY_SHOW_WHOLE_BRACKET_EXPRESSION, false)); setAnimateBracketMatching(config.readEntry(KEY_ANIMATE_BRACKET_MATCHING, false)); configEnd(); } void KateRendererConfig::writeConfig(KConfigGroup &config) { config.writeEntry(KEY_SCHEMA, schema()); config.writeEntry(KEY_WORD_WRAP_MARKER, wordWrapMarker()); config.writeEntry(KEY_SHOW_INDENTATION_LINES, showIndentationLines()); config.writeEntry(KEY_SHOW_WHOLE_BRACKET_EXPRESSION, showWholeBracketExpression()); config.writeEntry(KEY_ANIMATE_BRACKET_MATCHING, animateBracketMatching()); } void KateRendererConfig::updateConfig() { if (m_renderer) { m_renderer->updateConfig(); return; } if (isGlobal()) { for (int z = 0; z < KTextEditor::EditorPrivate::self()->views().size(); ++z) { (KTextEditor::EditorPrivate::self()->views())[z]->renderer()->updateConfig(); } // write config KConfigGroup cg(KTextEditor::EditorPrivate::config(), "Renderer"); writeConfig(cg); KTextEditor::EditorPrivate::config()->sync(); } } const QString &KateRendererConfig::schema() const { if (m_schemaSet || isGlobal()) { return m_schema; } return s_global->schema(); } void KateRendererConfig::setSchema(const QString &schema) { if (m_schemaSet && m_schema == schema) { return; } configStart(); m_schemaSet = true; m_schema = schema; setSchemaInternal(schema); configEnd(); } void KateRendererConfig::reloadSchema() { if (isGlobal()) { setSchemaInternal(m_schema); foreach (KTextEditor::ViewPrivate *view, KTextEditor::EditorPrivate::self()->views()) { view->renderer()->config()->reloadSchema(); } } else if (m_renderer && m_schemaSet) { setSchemaInternal(m_schema); } // trigger renderer/view update if (m_renderer) { m_renderer->updateConfig(); } } void KateRendererConfig::setSchemaInternal(const QString &schema) { m_schemaSet = true; m_schema = schema; KConfigGroup config = KTextEditor::EditorPrivate::self()->schemaManager()->schema(schema); // use global color instance, creation is expensive! const KateDefaultColors &colors(KTextEditor::EditorPrivate::self()->defaultColors()); m_backgroundColor = config.readEntry("Color Background", colors.color(Kate::Background)); m_backgroundColorSet = true; m_selectionColor = config.readEntry("Color Selection", colors.color(Kate::SelectionBackground)); m_selectionColorSet = true; m_highlightedLineColor = config.readEntry("Color Highlighted Line", colors.color(Kate::HighlightedLineBackground)); m_highlightedLineColorSet = true; m_highlightedBracketColor = config.readEntry("Color Highlighted Bracket", colors.color(Kate::HighlightedBracket)); m_highlightedBracketColorSet = true; m_wordWrapMarkerColor = config.readEntry("Color Word Wrap Marker", colors.color(Kate::WordWrapMarker)); m_wordWrapMarkerColorSet = true; m_tabMarkerColor = config.readEntry("Color Tab Marker", colors.color(Kate::TabMarker)); m_tabMarkerColorSet = true; m_indentationLineColor = config.readEntry("Color Indentation Line", colors.color(Kate::IndentationLine)); m_indentationLineColorSet = true; m_iconBarColor = config.readEntry("Color Icon Bar", colors.color(Kate::IconBar)); m_iconBarColorSet = true; m_foldingColor = config.readEntry("Color Code Folding", colors.color(Kate::CodeFolding)); m_foldingColorSet = true; m_lineNumberColor = config.readEntry("Color Line Number", colors.color(Kate::LineNumber)); m_lineNumberColorSet = true; m_currentLineNumberColor = config.readEntry("Color Current Line Number", colors.color(Kate::CurrentLineNumber)); m_currentLineNumberColorSet = true; m_separatorColor = config.readEntry("Color Separator", colors.color(Kate::Separator)); m_separatorColorSet = true; m_spellingMistakeLineColor = config.readEntry("Color Spelling Mistake Line", colors.color(Kate::SpellingMistakeLine)); m_spellingMistakeLineColorSet = true; m_modifiedLineColor = config.readEntry("Color Modified Lines", colors.color(Kate::ModifiedLine)); m_modifiedLineColorSet = true; m_savedLineColor = config.readEntry("Color Saved Lines", colors.color(Kate::SavedLine)); m_savedLineColorSet = true; m_searchHighlightColor = config.readEntry("Color Search Highlight", colors.color(Kate::SearchHighlight)); m_searchHighlightColorSet = true; m_replaceHighlightColor = config.readEntry("Color Replace Highlight", colors.color(Kate::ReplaceHighlight)); m_replaceHighlightColorSet = true; for (int i = Kate::FIRST_MARK; i <= Kate::LAST_MARK; i++) { QColor col = config.readEntry(QStringLiteral("Color MarkType %1").arg(i + 1), colors.mark(i)); m_lineMarkerColorSet[i] = true; m_lineMarkerColor[i] = col; } QFont f(QFontDatabase::systemFont(QFontDatabase::FixedFont)); m_font = config.readEntry("Font", f); m_fontMetrics = QFontMetricsF(m_font); m_fontSet = true; m_templateBackgroundColor = config.readEntry(QStringLiteral("Color Template Background"), colors.color(Kate::TemplateBackground)); m_templateFocusedEditablePlaceholderColor = config.readEntry(QStringLiteral("Color Template Focused Editable Placeholder"), colors.color(Kate::TemplateFocusedEditablePlaceholder)); m_templateEditablePlaceholderColor = config.readEntry(QStringLiteral("Color Template Editable Placeholder"), colors.color(Kate::TemplateEditablePlaceholder)); m_templateNotEditablePlaceholderColor = config.readEntry(QStringLiteral("Color Template Not Editable Placeholder"), colors.color(Kate::TemplateNotEditablePlaceholder)); m_templateColorsSet = true; } const QFont &KateRendererConfig::font() const { if (m_fontSet || isGlobal()) { return m_font; } return s_global->font(); } const QFontMetricsF &KateRendererConfig::fontMetrics() const { if (m_fontSet || isGlobal()) { return m_fontMetrics; } return s_global->fontMetrics(); } void KateRendererConfig::setFont(const QFont &font) { if (m_fontSet && m_font == font) { return; } configStart(); m_fontSet = true; m_font = font; m_fontMetrics = QFontMetricsF(m_font); configEnd(); } bool KateRendererConfig::wordWrapMarker() const { if (m_wordWrapMarkerSet || isGlobal()) { return m_wordWrapMarker; } return s_global->wordWrapMarker(); } void KateRendererConfig::setWordWrapMarker(bool on) { if (m_wordWrapMarkerSet && m_wordWrapMarker == on) { return; } configStart(); m_wordWrapMarkerSet = true; m_wordWrapMarker = on; configEnd(); } const QColor &KateRendererConfig::backgroundColor() const { if (m_backgroundColorSet || isGlobal()) { return m_backgroundColor; } return s_global->backgroundColor(); } void KateRendererConfig::setBackgroundColor(const QColor &col) { if (m_backgroundColorSet && m_backgroundColor == col) { return; } configStart(); m_backgroundColorSet = true; m_backgroundColor = col; configEnd(); } const QColor &KateRendererConfig::selectionColor() const { if (m_selectionColorSet || isGlobal()) { return m_selectionColor; } return s_global->selectionColor(); } void KateRendererConfig::setSelectionColor(const QColor &col) { if (m_selectionColorSet && m_selectionColor == col) { return; } configStart(); m_selectionColorSet = true; m_selectionColor = col; configEnd(); } const QColor &KateRendererConfig::highlightedLineColor() const { if (m_highlightedLineColorSet || isGlobal()) { return m_highlightedLineColor; } return s_global->highlightedLineColor(); } void KateRendererConfig::setHighlightedLineColor(const QColor &col) { if (m_highlightedLineColorSet && m_highlightedLineColor == col) { return; } configStart(); m_highlightedLineColorSet = true; m_highlightedLineColor = col; configEnd(); } const QColor &KateRendererConfig::lineMarkerColor(KTextEditor::MarkInterface::MarkTypes type) const { int index = 0; if (type > 0) { while ((type >> index++) ^ 1) {} } index -= 1; if (index < 0 || index >= KTextEditor::MarkInterface::reservedMarkersCount()) { static QColor dummy; return dummy; } if (m_lineMarkerColorSet[index] || isGlobal()) { return m_lineMarkerColor[index]; } return s_global->lineMarkerColor(type); } void KateRendererConfig::setLineMarkerColor(const QColor &col, KTextEditor::MarkInterface::MarkTypes type) { int index = static_cast(log(static_cast(type)) / log(2.0)); Q_ASSERT(index >= 0 && index < KTextEditor::MarkInterface::reservedMarkersCount()); if (m_lineMarkerColorSet[index] && m_lineMarkerColor[index] == col) { return; } configStart(); m_lineMarkerColorSet[index] = true; m_lineMarkerColor[index] = col; configEnd(); } const QColor &KateRendererConfig::highlightedBracketColor() const { if (m_highlightedBracketColorSet || isGlobal()) { return m_highlightedBracketColor; } return s_global->highlightedBracketColor(); } void KateRendererConfig::setHighlightedBracketColor(const QColor &col) { if (m_highlightedBracketColorSet && m_highlightedBracketColor == col) { return; } configStart(); m_highlightedBracketColorSet = true; m_highlightedBracketColor = col; configEnd(); } const QColor &KateRendererConfig::wordWrapMarkerColor() const { if (m_wordWrapMarkerColorSet || isGlobal()) { return m_wordWrapMarkerColor; } return s_global->wordWrapMarkerColor(); } void KateRendererConfig::setWordWrapMarkerColor(const QColor &col) { if (m_wordWrapMarkerColorSet && m_wordWrapMarkerColor == col) { return; } configStart(); m_wordWrapMarkerColorSet = true; m_wordWrapMarkerColor = col; configEnd(); } const QColor &KateRendererConfig::tabMarkerColor() const { if (m_tabMarkerColorSet || isGlobal()) { return m_tabMarkerColor; } return s_global->tabMarkerColor(); } void KateRendererConfig::setTabMarkerColor(const QColor &col) { if (m_tabMarkerColorSet && m_tabMarkerColor == col) { return; } configStart(); m_tabMarkerColorSet = true; m_tabMarkerColor = col; configEnd(); } const QColor &KateRendererConfig::indentationLineColor() const { if (m_indentationLineColorSet || isGlobal()) { return m_indentationLineColor; } return s_global->indentationLineColor(); } void KateRendererConfig::setIndentationLineColor(const QColor &col) { if (m_indentationLineColorSet && m_indentationLineColor == col) { return; } configStart(); m_indentationLineColorSet = true; m_indentationLineColor = col; configEnd(); } const QColor &KateRendererConfig::iconBarColor() const { if (m_iconBarColorSet || isGlobal()) { return m_iconBarColor; } return s_global->iconBarColor(); } void KateRendererConfig::setIconBarColor(const QColor &col) { if (m_iconBarColorSet && m_iconBarColor == col) { return; } configStart(); m_iconBarColorSet = true; m_iconBarColor = col; configEnd(); } const QColor &KateRendererConfig::foldingColor() const { if (m_foldingColorSet || isGlobal()) { return m_foldingColor; } return s_global->foldingColor(); } void KateRendererConfig::setFoldingColor(const QColor &col) { if (m_foldingColorSet && m_foldingColor == col) { return; } configStart(); m_foldingColorSet = true; m_foldingColor = col; configEnd(); } const QColor &KateRendererConfig::templateBackgroundColor() const { if (m_templateColorsSet || isGlobal()) { return m_templateBackgroundColor; } return s_global->templateBackgroundColor(); } const QColor &KateRendererConfig::templateEditablePlaceholderColor() const { if (m_templateColorsSet || isGlobal()) { return m_templateEditablePlaceholderColor; } return s_global->templateEditablePlaceholderColor(); } const QColor &KateRendererConfig::templateFocusedEditablePlaceholderColor() const { if (m_templateColorsSet || isGlobal()) { return m_templateFocusedEditablePlaceholderColor; } return s_global->templateFocusedEditablePlaceholderColor(); } const QColor &KateRendererConfig::templateNotEditablePlaceholderColor() const { if (m_templateColorsSet || isGlobal()) { return m_templateNotEditablePlaceholderColor; } return s_global->templateNotEditablePlaceholderColor(); } const QColor &KateRendererConfig::lineNumberColor() const { if (m_lineNumberColorSet || isGlobal()) { return m_lineNumberColor; } return s_global->lineNumberColor(); } void KateRendererConfig::setLineNumberColor(const QColor &col) { if (m_lineNumberColorSet && m_lineNumberColor == col) { return; } configStart(); m_lineNumberColorSet = true; m_lineNumberColor = col; configEnd(); } const QColor &KateRendererConfig::currentLineNumberColor() const { if (m_currentLineNumberColorSet || isGlobal()) { return m_currentLineNumberColor; } return s_global->currentLineNumberColor(); } void KateRendererConfig::setCurrentLineNumberColor(const QColor &col) { if (m_currentLineNumberColorSet && m_currentLineNumberColor == col) { return; } configStart(); m_currentLineNumberColorSet = true; m_currentLineNumberColor = col; configEnd(); } const QColor &KateRendererConfig::separatorColor() const { if (m_separatorColorSet || isGlobal()) { return m_separatorColor; } return s_global->separatorColor(); } void KateRendererConfig::setSeparatorColor(const QColor &col) { if (m_separatorColorSet && m_separatorColor == col) { return; } configStart(); m_separatorColorSet = true; m_separatorColor = col; configEnd(); } const QColor &KateRendererConfig::spellingMistakeLineColor() const { if (m_spellingMistakeLineColorSet || isGlobal()) { return m_spellingMistakeLineColor; } return s_global->spellingMistakeLineColor(); } void KateRendererConfig::setSpellingMistakeLineColor(const QColor &col) { if (m_spellingMistakeLineColorSet && m_spellingMistakeLineColor == col) { return; } configStart(); m_spellingMistakeLineColorSet = true; m_spellingMistakeLineColor = col; configEnd(); } const QColor &KateRendererConfig::modifiedLineColor() const { if (m_modifiedLineColorSet || isGlobal()) { return m_modifiedLineColor; } return s_global->modifiedLineColor(); } void KateRendererConfig::setModifiedLineColor(const QColor &col) { if (m_modifiedLineColorSet && m_modifiedLineColor == col) { return; } configStart(); m_modifiedLineColorSet = true; m_modifiedLineColor = col; configEnd(); } const QColor &KateRendererConfig::savedLineColor() const { if (m_savedLineColorSet || isGlobal()) { return m_savedLineColor; } return s_global->savedLineColor(); } void KateRendererConfig::setSavedLineColor(const QColor &col) { if (m_savedLineColorSet && m_savedLineColor == col) { return; } configStart(); m_savedLineColorSet = true; m_savedLineColor = col; configEnd(); } const QColor &KateRendererConfig::searchHighlightColor() const { if (m_searchHighlightColorSet || isGlobal()) { return m_searchHighlightColor; } return s_global->searchHighlightColor(); } void KateRendererConfig::setSearchHighlightColor(const QColor &col) { if (m_searchHighlightColorSet && m_searchHighlightColor == col) { return; } configStart(); m_searchHighlightColorSet = true; m_searchHighlightColor = col; configEnd(); } const QColor &KateRendererConfig::replaceHighlightColor() const { if (m_replaceHighlightColorSet || isGlobal()) { return m_replaceHighlightColor; } return s_global->replaceHighlightColor(); } void KateRendererConfig::setReplaceHighlightColor(const QColor &col) { if (m_replaceHighlightColorSet && m_replaceHighlightColor == col) { return; } configStart(); m_replaceHighlightColorSet = true; m_replaceHighlightColor = col; configEnd(); } bool KateRendererConfig::showIndentationLines() const { if (m_showIndentationLinesSet || isGlobal()) { return m_showIndentationLines; } return s_global->showIndentationLines(); } void KateRendererConfig::setShowIndentationLines(bool on) { if (m_showIndentationLinesSet && m_showIndentationLines == on) { return; } configStart(); m_showIndentationLinesSet = true; m_showIndentationLines = on; configEnd(); } bool KateRendererConfig::showWholeBracketExpression() const { if (m_showWholeBracketExpressionSet || isGlobal()) { return m_showWholeBracketExpression; } return s_global->showWholeBracketExpression(); } void KateRendererConfig::setShowWholeBracketExpression(bool on) { if (m_showWholeBracketExpressionSet && m_showWholeBracketExpression == on) { return; } configStart(); m_showWholeBracketExpressionSet = true; m_showWholeBracketExpression = on; configEnd(); } bool KateRendererConfig::animateBracketMatching() const { return s_global->m_animateBracketMatching; } void KateRendererConfig::setAnimateBracketMatching(bool on) { if (!isGlobal()) { s_global->setAnimateBracketMatching(on); } else if (on != m_animateBracketMatching) { configStart(); m_animateBracketMatching = on; configEnd(); } } //END diff --git a/src/utils/kateconfig.h b/src/utils/kateconfig.h index a6a41600..1db82aaf 100644 --- a/src/utils/kateconfig.h +++ b/src/utils/kateconfig.h @@ -1,849 +1,853 @@ /* This file is part of the KDE libraries Copyright (C) 2003 Christoph Cullmann This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef __KATE_CONFIG_H__ #define __KATE_CONFIG_H__ #include #include #include "ktexteditor/view.h" #include #include #include #include #include #include class KConfigGroup; namespace KTextEditor { class ViewPrivate; } namespace KTextEditor { class DocumentPrivate; } class KateRenderer; namespace KTextEditor { class EditorPrivate; } class KConfig; class QTextCodec; /** * Base Class for the Kate Config Classes */ class KateConfig { public: /** * Default Constructor */ KateConfig(); /** * Virtual Destructor */ virtual ~KateConfig(); public: /** * start some config changes * this method is needed to init some kind of transaction * for config changes, update will only be done once, at * configEnd() call */ void configStart(); /** * end a config change transaction, update the concerned * documents/views/renderers */ void configEnd(); protected: /** * do the real update */ virtual void updateConfig() = 0; private: /** * recursion depth */ uint configSessionNumber; /** * is a config session running */ bool configIsRunning; }; class KTEXTEDITOR_EXPORT KateGlobalConfig : public KateConfig { private: friend class KTextEditor::EditorPrivate; /** * only used in KTextEditor::EditorPrivate for the static global fallback !!! */ KateGlobalConfig(); /** * Destructor */ ~KateGlobalConfig(); public: static KateGlobalConfig *global() { return s_global; } public: /** * Read config from object */ void readConfig(const KConfigGroup &config); /** * Write config to object */ void writeConfig(KConfigGroup &config); protected: void updateConfig() Q_DECL_OVERRIDE; public: KEncodingProber::ProberType proberType() const { return m_proberType; } void setProberType(KEncodingProber::ProberType proberType); QTextCodec *fallbackCodec() const; const QString &fallbackEncoding() const; bool setFallbackEncoding(const QString &encoding); private: KEncodingProber::ProberType m_proberType; QString m_fallbackEncoding; private: static KateGlobalConfig *s_global; }; class KTEXTEDITOR_EXPORT KateDocumentConfig : public KateConfig { private: friend class KTextEditor::EditorPrivate; KateDocumentConfig(); public: KateDocumentConfig(const KConfigGroup &cg); /** * Construct a DocumentConfig */ KateDocumentConfig(KTextEditor::DocumentPrivate *doc); /** * Cu DocumentConfig */ ~KateDocumentConfig(); inline static KateDocumentConfig *global() { return s_global; } inline bool isGlobal() const { return (this == global()); } public: /** * Read config from object */ void readConfig(const KConfigGroup &config); /** * Write config to object */ void writeConfig(KConfigGroup &config); protected: void updateConfig() Q_DECL_OVERRIDE; public: int tabWidth() const; void setTabWidth(int tabWidth); int indentationWidth() const; void setIndentationWidth(int indentationWidth); const QString &indentationMode() const; void setIndentationMode(const QString &identationMode); enum TabHandling { tabInsertsTab = 0, tabIndents = 1, tabSmart = 2 //!< indents in leading space, otherwise inserts tab }; uint tabHandling() const; void setTabHandling(uint tabHandling); bool wordWrap() const; void setWordWrap(bool on); int wordWrapAt() const; void setWordWrapAt(int col); bool pageUpDownMovesCursor() const; void setPageUpDownMovesCursor(bool on); void setKeepExtraSpaces(bool on); bool keepExtraSpaces() const; void setIndentPastedText(bool on); bool indentPastedText() const; void setBackspaceIndents(bool on); bool backspaceIndents() const; void setSmartHome(bool on); bool smartHome() const; void setShowTabs(bool on); bool showTabs() const; void setShowSpaces(bool on); bool showSpaces() const; + void setMarkerSize(uint markerSize); + uint markerSize() const; + void setReplaceTabsDyn(bool on); bool replaceTabsDyn() const; /** * Remove trailing spaces on save. * triState = 0: never remove trailing spaces * triState = 1: remove trailing spaces of modified lines (line modification system) * triState = 2: remove trailing spaces in entire document */ void setRemoveSpaces(int triState); int removeSpaces() const; void setNewLineAtEof(bool on); bool newLineAtEof() const; void setOvr(bool on); bool ovr() const; void setTabIndents(bool on); bool tabIndentsEnabled() const; QTextCodec *codec() const; const QString &encoding() const; bool setEncoding(const QString &encoding); bool isSetEncoding() const; enum Eol { eolUnix = 0, eolDos = 1, eolMac = 2 }; int eol() const; QString eolString(); void setEol(int mode); bool bom() const; void setBom(bool bom); bool allowEolDetection() const; void setAllowEolDetection(bool on); enum BackupFlags { LocalFiles = 1, RemoteFiles = 2 }; uint backupFlags() const; void setBackupFlags(uint flags); const QString &backupPrefix() const; void setBackupPrefix(const QString &prefix); const QString &backupSuffix() const; void setBackupSuffix(const QString &suffix); const QString &swapDirectory() const; void setSwapDirectory(const QString &directory); enum SwapFileMode { DisableSwapFile = 0, EnableSwapFile, SwapFilePresetDirectory }; uint swapFileModeRaw() const; SwapFileMode swapFileMode() const; void setSwapFileMode(uint mode); uint swapSyncInterval() const; void setSwapSyncInterval(uint interval); bool onTheFlySpellCheck() const; void setOnTheFlySpellCheck(bool on); int lineLengthLimit() const; void setLineLengthLimit(int limit); private: QString m_indentationMode; int m_indentationWidth; int m_tabWidth; uint m_tabHandling; uint m_configFlags; int m_wordWrapAt; bool m_wordWrap; bool m_pageUpDownMovesCursor; bool m_allowEolDetection; int m_eol; bool m_bom; uint m_backupFlags; QString m_encoding; QString m_backupPrefix; QString m_backupSuffix; uint m_swapFileMode; QString m_swapDirectory; uint m_swapSyncInterval; bool m_onTheFlySpellCheck; int m_lineLengthLimit; bool m_tabWidthSet : 1; bool m_indentationWidthSet : 1; bool m_indentationModeSet : 1; bool m_wordWrapSet : 1; bool m_wordWrapAtSet : 1; bool m_pageUpDownMovesCursorSet : 1; bool m_keepExtraSpacesSet : 1; bool m_keepExtraSpaces : 1; bool m_indentPastedTextSet : 1; bool m_indentPastedText : 1; bool m_backspaceIndentsSet : 1; bool m_backspaceIndents : 1; bool m_smartHomeSet : 1; bool m_smartHome : 1; bool m_showTabsSet : 1; bool m_showTabs : 1; bool m_showSpacesSet : 1; bool m_showSpaces : 1; + uint m_markerSize; bool m_replaceTabsDynSet : 1; bool m_replaceTabsDyn : 1; bool m_removeSpacesSet : 1; uint m_removeSpaces : 2; bool m_newLineAtEofSet : 1; bool m_newLineAtEof : 1; bool m_overwiteModeSet : 1; bool m_overwiteMode : 1; bool m_tabIndentsSet : 1; bool m_tabIndents : 1; bool m_encodingSet : 1; bool m_eolSet : 1; bool m_bomSet : 1; bool m_allowEolDetectionSet : 1; bool m_backupFlagsSet : 1; bool m_backupPrefixSet : 1; bool m_backupSuffixSet : 1; bool m_swapFileModeSet : 1; bool m_swapDirectorySet : 1; bool m_swapSyncIntervalSet : 1; bool m_onTheFlySpellCheckSet : 1; bool m_lineLengthLimitSet : 1; private: static KateDocumentConfig *s_global; KTextEditor::DocumentPrivate *m_doc; }; class KTEXTEDITOR_EXPORT KateViewConfig : public KateConfig { private: friend class KTextEditor::EditorPrivate; /** * only used in KTextEditor::EditorPrivate for the static global fallback !!! */ KateViewConfig(); public: /** * Construct a DocumentConfig */ explicit KateViewConfig(KTextEditor::ViewPrivate *view); /** * Cu DocumentConfig */ ~KateViewConfig(); inline static KateViewConfig *global() { return s_global; } inline bool isGlobal() const { return (this == global()); } public: /** * Read config from object */ void readConfig(const KConfigGroup &config); /** * Write config to object */ void writeConfig(KConfigGroup &config); protected: void updateConfig() Q_DECL_OVERRIDE; public: bool dynWordWrapSet() const { return m_dynWordWrapSet; } bool dynWordWrap() const; void setDynWordWrap(bool wrap); int dynWordWrapIndicators() const; void setDynWordWrapIndicators(int mode); int dynWordWrapAlignIndent() const; void setDynWordWrapAlignIndent(int indent); bool lineNumbers() const; void setLineNumbers(bool on); bool scrollBarMarks() const; void setScrollBarMarks(bool on); bool scrollBarPreview() const; void setScrollBarPreview(bool on); bool scrollBarMiniMap() const; void setScrollBarMiniMap(bool on); bool scrollBarMiniMapAll() const; void setScrollBarMiniMapAll(bool on); int scrollBarMiniMapWidth() const; void setScrollBarMiniMapWidth(int width); /* Whether to show scrollbars */ enum ScrollbarMode { AlwaysOn = 0, ShowWhenNeeded, AlwaysOff }; int showScrollbars() const; void setShowScrollbars(int mode); bool iconBar() const; void setIconBar(bool on); bool foldingBar() const; void setFoldingBar(bool on); bool foldingPreview() const; void setFoldingPreview(bool on); bool lineModification() const; void setLineModification(bool on); int bookmarkSort() const; void setBookmarkSort(int mode); int autoCenterLines() const; void setAutoCenterLines(int lines); enum SearchFlags { IncMatchCase = 1 << 0, IncHighlightAll = 1 << 1, IncFromCursor = 1 << 2, PowerMatchCase = 1 << 3, PowerHighlightAll = 1 << 4, PowerFromCursor = 1 << 5, // PowerSelectionOnly = 1 << 6, Better not save to file // Sebastian PowerModePlainText = 1 << 7, PowerModeWholeWords = 1 << 8, PowerModeEscapeSequences = 1 << 9, PowerModeRegularExpression = 1 << 10, PowerUsePlaceholders = 1 << 11 }; long searchFlags() const; void setSearchFlags(long flags); int maxHistorySize() const; uint defaultMarkType() const; void setDefaultMarkType(uint type); bool allowMarkMenu() const; void setAllowMarkMenu(bool allow); bool persistentSelection() const; void setPersistentSelection(bool on); KTextEditor::View::InputMode inputMode() const; void setInputMode(KTextEditor::View::InputMode mode); void setInputModeRaw(int rawmode); bool viInputModeStealKeys() const; void setViInputModeStealKeys(bool on); bool viRelativeLineNumbers() const; void setViRelativeLineNumbers(bool on); // Do we still need the enum and related functions below? enum TextToSearch { Nowhere = 0, SelectionOnly = 1, SelectionWord = 2, WordOnly = 3, WordSelection = 4 }; bool automaticCompletionInvocation() const; void setAutomaticCompletionInvocation(bool on); bool wordCompletion() const; void setWordCompletion(bool on); bool keywordCompletion () const; void setKeywordCompletion (bool on); int wordCompletionMinimalWordLength() const; void setWordCompletionMinimalWordLength(int length); bool wordCompletionRemoveTail() const; void setWordCompletionRemoveTail(bool on); bool smartCopyCut() const; void setSmartCopyCut(bool on); bool scrollPastEnd() const; void setScrollPastEnd(bool on); bool foldFirstLine() const; void setFoldFirstLine(bool on); bool showWordCount(); void setShowWordCount(bool on); bool autoBrackets() const; void setAutoBrackets(bool on); private: bool m_dynWordWrap; int m_dynWordWrapIndicators; int m_dynWordWrapAlignIndent; bool m_lineNumbers; bool m_scrollBarMarks; bool m_scrollBarPreview; bool m_scrollBarMiniMap; bool m_scrollBarMiniMapAll; int m_scrollBarMiniMapWidth; int m_showScrollbars; bool m_iconBar; bool m_foldingBar; bool m_foldingPreview; bool m_lineModification; int m_bookmarkSort; int m_autoCenterLines; long m_searchFlags; int m_maxHistorySize; uint m_defaultMarkType; bool m_persistentSelection; KTextEditor::View::InputMode m_inputMode; bool m_viInputModeStealKeys; bool m_viRelativeLineNumbers; bool m_automaticCompletionInvocation; bool m_wordCompletion; bool m_keywordCompletion; int m_wordCompletionMinimalWordLength; bool m_wordCompletionRemoveTail; bool m_smartCopyCut; bool m_scrollPastEnd; bool m_foldFirstLine; bool m_showWordCount; bool m_autoBrackets; bool m_dynWordWrapSet : 1; bool m_dynWordWrapIndicatorsSet : 1; bool m_dynWordWrapAlignIndentSet : 1; bool m_lineNumbersSet : 1; bool m_scrollBarMarksSet : 1; bool m_scrollBarPreviewSet : 1; bool m_scrollBarMiniMapSet : 1; bool m_scrollBarMiniMapAllSet : 1; bool m_scrollBarMiniMapWidthSet : 1; bool m_showScrollbarsSet : 1; bool m_iconBarSet : 1; bool m_foldingBarSet : 1; bool m_foldingPreviewSet : 1; bool m_lineModificationSet : 1; bool m_bookmarkSortSet : 1; bool m_autoCenterLinesSet : 1; bool m_searchFlagsSet : 1; bool m_defaultMarkTypeSet : 1; bool m_persistentSelectionSet : 1; bool m_inputModeSet : 1; bool m_viInputModeStealKeysSet : 1; bool m_viRelativeLineNumbersSet : 1; bool m_automaticCompletionInvocationSet : 1; bool m_wordCompletionSet : 1; bool m_keywordCompletionSet : 1; bool m_wordCompletionMinimalWordLengthSet : 1; bool m_smartCopyCutSet : 1; bool m_scrollPastEndSet : 1; bool m_allowMarkMenu : 1; bool m_wordCompletionRemoveTailSet : 1; bool m_foldFirstLineSet : 1; bool m_autoBracketsSet : 1; private: static KateViewConfig *s_global; KTextEditor::ViewPrivate *m_view; }; class KTEXTEDITOR_EXPORT KateRendererConfig : public KateConfig { private: friend class KTextEditor::EditorPrivate; /** * only used in KTextEditor::EditorPrivate for the static global fallback !!! */ KateRendererConfig(); public: /** * Construct a DocumentConfig */ KateRendererConfig(KateRenderer *renderer); /** * Cu DocumentConfig */ ~KateRendererConfig(); inline static KateRendererConfig *global() { return s_global; } inline bool isGlobal() const { return (this == global()); } public: /** * Read config from object */ void readConfig(const KConfigGroup &config); /** * Write config to object */ void writeConfig(KConfigGroup &config); protected: void updateConfig() Q_DECL_OVERRIDE; public: const QString &schema() const; void setSchema(const QString &schema); /** * Reload the schema from the schema manager. * For the global instance, have all other instances reload. * Used by the schema config page to apply changes. */ void reloadSchema(); const QFont &font() const; const QFontMetricsF &fontMetrics() const; void setFont(const QFont &font); bool wordWrapMarker() const; void setWordWrapMarker(bool on); const QColor &backgroundColor() const; void setBackgroundColor(const QColor &col); const QColor &selectionColor() const; void setSelectionColor(const QColor &col); const QColor &highlightedLineColor() const; void setHighlightedLineColor(const QColor &col); const QColor &lineMarkerColor(KTextEditor::MarkInterface::MarkTypes type = KTextEditor::MarkInterface::markType01) const; // markType01 == Bookmark void setLineMarkerColor(const QColor &col, KTextEditor::MarkInterface::MarkTypes type = KTextEditor::MarkInterface::markType01); const QColor &highlightedBracketColor() const; void setHighlightedBracketColor(const QColor &col); const QColor &wordWrapMarkerColor() const; void setWordWrapMarkerColor(const QColor &col); const QColor &tabMarkerColor() const; void setTabMarkerColor(const QColor &col); const QColor &indentationLineColor() const; void setIndentationLineColor(const QColor &col); const QColor &iconBarColor() const; void setIconBarColor(const QColor &col); const QColor &foldingColor() const; void setFoldingColor(const QColor &col); // the line number color is used for the line numbers on the left bar const QColor &lineNumberColor() const; void setLineNumberColor(const QColor &col); const QColor ¤tLineNumberColor() const; void setCurrentLineNumberColor(const QColor &col); // the color of the separator between line numbers and icon bar const QColor &separatorColor() const; void setSeparatorColor(const QColor &col); const QColor &spellingMistakeLineColor() const; void setSpellingMistakeLineColor(const QColor &col); bool showIndentationLines() const; void setShowIndentationLines(bool on); bool showWholeBracketExpression() const; void setShowWholeBracketExpression(bool on); bool animateBracketMatching() const; void setAnimateBracketMatching(bool on); const QColor &templateBackgroundColor() const; const QColor &templateEditablePlaceholderColor() const; const QColor &templateFocusedEditablePlaceholderColor() const; const QColor &templateNotEditablePlaceholderColor() const; const QColor &modifiedLineColor() const; void setModifiedLineColor(const QColor &col); const QColor &savedLineColor() const; void setSavedLineColor(const QColor &col); const QColor &searchHighlightColor() const; void setSearchHighlightColor(const QColor &col); const QColor &replaceHighlightColor() const; void setReplaceHighlightColor(const QColor &col); private: /** * Read the schema properties from the config file. */ void setSchemaInternal(const QString &schema); QString m_schema; QFont m_font; QFontMetricsF m_fontMetrics; QColor m_backgroundColor; QColor m_selectionColor; QColor m_highlightedLineColor; QColor m_highlightedBracketColor; QColor m_wordWrapMarkerColor; QColor m_tabMarkerColor; QColor m_indentationLineColor; QColor m_iconBarColor; QColor m_foldingColor; QColor m_lineNumberColor; QColor m_currentLineNumberColor; QColor m_separatorColor; QColor m_spellingMistakeLineColor; QVector m_lineMarkerColor; QColor m_templateBackgroundColor; QColor m_templateEditablePlaceholderColor; QColor m_templateFocusedEditablePlaceholderColor; QColor m_templateNotEditablePlaceholderColor; QColor m_modifiedLineColor; QColor m_savedLineColor; QColor m_searchHighlightColor; QColor m_replaceHighlightColor; bool m_wordWrapMarker; bool m_showIndentationLines; bool m_showWholeBracketExpression; bool m_animateBracketMatching; bool m_schemaSet : 1; bool m_fontSet : 1; bool m_wordWrapMarkerSet : 1; bool m_showIndentationLinesSet : 1; bool m_showWholeBracketExpressionSet : 1; bool m_backgroundColorSet : 1; bool m_selectionColorSet : 1; bool m_highlightedLineColorSet : 1; bool m_highlightedBracketColorSet : 1; bool m_wordWrapMarkerColorSet : 1; bool m_tabMarkerColorSet : 1; bool m_indentationLineColorSet : 1; bool m_iconBarColorSet : 1; bool m_foldingColorSet : 1; bool m_lineNumberColorSet : 1; bool m_currentLineNumberColorSet : 1; bool m_separatorColorSet : 1; bool m_spellingMistakeLineColorSet : 1; bool m_templateColorsSet : 1; bool m_modifiedLineColorSet : 1; bool m_savedLineColorSet : 1; bool m_searchHighlightColorSet : 1; bool m_replaceHighlightColorSet : 1; QBitArray m_lineMarkerColorSet; private: static KateRendererConfig *s_global; KateRenderer *m_renderer; }; #endif diff --git a/src/view/kateviewinternal.cpp b/src/view/kateviewinternal.cpp index b664d5f1..daa91cb6 100644 --- a/src/view/kateviewinternal.cpp +++ b/src/view/kateviewinternal.cpp @@ -1,3777 +1,3778 @@ /* This file is part of the KDE libraries Copyright (C) 2002 John Firebaugh Copyright (C) 2002 Joseph Wenninger Copyright (C) 2002,2003 Christoph Cullmann Copyright (C) 2002-2007 Hamish Rodda Copyright (C) 2003 Anakim Border Copyright (C) 2007 Mirko Stocker Copyright (C) 2007 Matthew Woehlke Copyright (C) 2008 Erlend Hamberg Based on: KWriteView : Copyright (C) 1999 Jochen Wilhelmy This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kateviewinternal.h" #include "kateview.h" #include "kateviewhelpers.h" #include "katehighlight.h" #include "katebuffer.h" #include "katerenderer.h" #include "kateconfig.h" #include "katelayoutcache.h" #include "katecompletionwidget.h" #include "spellcheck/spellingmenu.h" #include "kateviewaccessible.h" #include "katetextanimation.h" #include "katemessagewidget.h" #include "kateglobal.h" #include "kateabstractinputmodefactory.h" #include "kateabstractinputmode.h" #include "katepartdebug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const bool debugPainting = false; KateViewInternal::KateViewInternal(KTextEditor::ViewPrivate *view) : QWidget(view) , editSessionNumber(0) , editIsRunning(false) , m_view(view) , m_cursor(doc()->buffer(), KTextEditor::Cursor(0, 0), Kate::TextCursor::MoveOnInsert) , m_mouse() , m_possibleTripleClick(false) , m_completionItemExpanded(false) , m_bm(doc()->newMovingRange(KTextEditor::Range::invalid(), KTextEditor::MovingRange::DoNotExpand)) , m_bmStart(doc()->newMovingRange(KTextEditor::Range::invalid(), KTextEditor::MovingRange::DoNotExpand)) , m_bmEnd(doc()->newMovingRange(KTextEditor::Range::invalid(), KTextEditor::MovingRange::DoNotExpand)) , m_bmLastFlashPos(doc()->newMovingCursor(KTextEditor::Cursor::invalid())) , m_dummy(nullptr) // stay on cursor will avoid that the view scroll around on press return at beginning , m_startPos(doc()->buffer(), KTextEditor::Cursor(0, 0), Kate::TextCursor::StayOnInsert) , m_visibleLineCount(0) , m_madeVisible(false) , m_shiftKeyPressed(false) , m_autoCenterLines(0) , m_minLinesVisible(0) , m_selChangedByUser(false) , m_selectAnchor(-1, -1) , m_selectionMode(Default) , m_layoutCache(new KateLayoutCache(renderer(), this)) , m_preserveX(false) , m_preservedX(0) , m_cachedMaxStartPos(-1, -1) , m_dragScrollTimer(this) , m_scrollTimer(this) , m_cursorTimer(this) , m_textHintTimer(this) , m_textHintDelay(500) , m_textHintPos(-1, -1) , m_imPreeditRange(nullptr) { QList factories = KTextEditor::EditorPrivate::self()->inputModeFactories(); Q_FOREACH(KateAbstractInputModeFactory *factory, factories) { KateAbstractInputMode *m = factory->createInputMode(this); m_inputModes.insert(m->viewInputMode(), m); } m_currentInputMode = m_inputModes[KTextEditor::View::NormalInputMode]; // TODO: twisted, but needed setMinimumSize(0, 0); setAttribute(Qt::WA_OpaquePaintEvent); setAttribute(Qt::WA_InputMethodEnabled); // invalidate m_selectionCached.start(), or keyb selection is screwed initially m_selectionCached = KTextEditor::Range::invalid(); // bracket markers are only for this view and should not be printed m_bm->setView(m_view); m_bmStart->setView(m_view); m_bmEnd->setView(m_view); m_bm->setAttributeOnlyForViews(true); m_bmStart->setAttributeOnlyForViews(true); m_bmEnd->setAttributeOnlyForViews(true); // use z depth defined in moving ranges interface m_bm->setZDepth(-1000.0); m_bmStart->setZDepth(-1000.0); m_bmEnd->setZDepth(-1000.0); // update mark attributes updateBracketMarkAttributes(); // // scrollbar for lines // m_lineScroll = new KateScrollBar(Qt::Vertical, this); m_lineScroll->show(); m_lineScroll->setTracking(true); m_lineScroll->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding); // Hijack the line scroller's controls, so we can scroll nicely for word-wrap connect(m_lineScroll, SIGNAL(actionTriggered(int)), SLOT(scrollAction(int))); connect(m_lineScroll, SIGNAL(sliderMoved(int)), SLOT(scrollLines(int))); connect(m_lineScroll, SIGNAL(sliderMMBMoved(int)), SLOT(scrollLines(int))); connect(m_lineScroll, SIGNAL(valueChanged(int)), SLOT(scrollLines(int))); // // scrollbar for columns // m_columnScroll = new QScrollBar(Qt::Horizontal, m_view); if (m_view->dynWordWrap()) { m_columnScroll->hide(); } else { m_columnScroll->show(); } m_columnScroll->setTracking(true); m_startX = 0; connect(m_columnScroll, SIGNAL(valueChanged(int)), SLOT(scrollColumns(int))); // bottom corner box m_dummy = new QWidget(m_view); m_dummy->setFixedSize(m_lineScroll->width(), m_columnScroll->sizeHint().height()); m_dummy->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); if (m_view->dynWordWrap()) { m_dummy->hide(); } else { m_dummy->show(); } cache()->setWrap(m_view->dynWordWrap()); // // iconborder ;) // m_leftBorder = new KateIconBorder(this, m_view); m_leftBorder->show(); // update view if folding ranges change connect(&m_view->textFolding(), SIGNAL(foldingRangesChanged()), SLOT(slotRegionVisibilityChanged())); m_displayCursor.setPosition(0, 0); setAcceptDrops(true); // event filter installEventFilter(this); // set initial cursor m_mouseCursor = Qt::IBeamCursor; setCursor(m_mouseCursor); // call mouseMoveEvent also if no mouse button is pressed setMouseTracking(true); m_dragInfo.state = diNone; // timers connect(&m_dragScrollTimer, SIGNAL(timeout()), this, SLOT(doDragScroll())); connect(&m_scrollTimer, SIGNAL(timeout()), this, SLOT(scrollTimeout())); connect(&m_cursorTimer, SIGNAL(timeout()), this, SLOT(cursorTimeout())); connect(&m_textHintTimer, SIGNAL(timeout()), this, SLOT(textHintTimeout())); // selection changed to set anchor connect(m_view, SIGNAL(selectionChanged(KTextEditor::View*)), this, SLOT(viewSelectionChanged())); #ifndef QT_NO_ACCESSIBILITY QAccessible::installFactory(accessibleInterfaceFactory); #endif connect(doc(), &KTextEditor::DocumentPrivate::textInserted, this, &KateViewInternal::documentTextInserted); connect(doc(), &KTextEditor::DocumentPrivate::textRemoved, this, &KateViewInternal::documentTextRemoved); // update is called in KTextEditor::ViewPrivate, after construction and layout is over // but before any other kateviewinternal call } KateViewInternal::~KateViewInternal() { // delete text animation object here, otherwise it updates the view in its destructor delete m_textAnimation; #ifndef QT_NO_ACCESSIBILITY QAccessible::removeFactory(accessibleInterfaceFactory); #endif // kill preedit ranges delete m_imPreeditRange; qDeleteAll(m_imPreeditRangeChildren); qDeleteAll(m_inputModes); // delete bracket markers delete m_bm; delete m_bmStart; delete m_bmEnd; } void KateViewInternal::prepareForDynWrapChange() { // Which is the current view line? m_wrapChangeViewLine = cache()->displayViewLine(m_displayCursor, true); } void KateViewInternal::dynWrapChanged() { m_dummy->setFixedSize(m_lineScroll->width(), m_columnScroll->sizeHint().height()); if (m_view->dynWordWrap()) { m_columnScroll->hide(); m_dummy->hide(); } else { // column scrollbar + bottom corner box m_columnScroll->show(); m_dummy->show(); } cache()->setWrap(m_view->dynWordWrap()); updateView(); if (m_view->dynWordWrap()) { scrollColumns(0); } // Determine where the cursor should be to get the cursor on the same view line if (m_wrapChangeViewLine != -1) { KTextEditor::Cursor newStart = viewLineOffset(m_displayCursor, -m_wrapChangeViewLine); makeVisible(newStart, newStart.column(), true); } else { update(); } } KTextEditor::Cursor KateViewInternal::endPos() const { // Hrm, no lines laid out at all?? if (!cache()->viewCacheLineCount()) { return KTextEditor::Cursor(); } for (int i = qMin(linesDisplayed() - 1, cache()->viewCacheLineCount() - 1); i >= 0; i--) { const KateTextLayout &thisLine = cache()->viewLine(i); if (thisLine.line() == -1) { continue; } if (thisLine.virtualLine() >= m_view->textFolding().visibleLines()) { // Cache is too out of date return KTextEditor::Cursor(m_view->textFolding().visibleLines() - 1, doc()->lineLength(m_view->textFolding().visibleLineToLine(m_view->textFolding().visibleLines() - 1))); } return KTextEditor::Cursor(thisLine.virtualLine(), thisLine.wrap() ? thisLine.endCol() - 1 : thisLine.endCol()); } // can happen, if view is still invisible return KTextEditor::Cursor(); } int KateViewInternal::endLine() const { return endPos().line(); } KateTextLayout KateViewInternal::yToKateTextLayout(int y) const { if (y < 0 || y > size().height()) { return KateTextLayout::invalid(); } int range = y / renderer()->lineHeight(); // lineRanges is always bigger than 0, after the initial updateView call if (range >= 0 && range < cache()->viewCacheLineCount()) { return cache()->viewLine(range); } return KateTextLayout::invalid(); } int KateViewInternal::lineToY(int viewLine) const { return (viewLine - startLine()) * renderer()->lineHeight(); } void KateViewInternal::slotIncFontSizes(qreal step) { renderer()->increaseFontSizes(step); } void KateViewInternal::slotDecFontSizes(qreal step) { renderer()->decreaseFontSizes(step); } /** * Line is the real line number to scroll to. */ void KateViewInternal::scrollLines(int line) { KTextEditor::Cursor newPos(line, 0); scrollPos(newPos); } // This can scroll less than one true line void KateViewInternal::scrollViewLines(int offset) { KTextEditor::Cursor c = viewLineOffset(startPos(), offset); scrollPos(c); bool blocked = m_lineScroll->blockSignals(true); m_lineScroll->setValue(startLine()); m_lineScroll->blockSignals(blocked); } void KateViewInternal::scrollAction(int action) { switch (action) { case QAbstractSlider::SliderSingleStepAdd: scrollNextLine(); break; case QAbstractSlider::SliderSingleStepSub: scrollPrevLine(); break; case QAbstractSlider::SliderPageStepAdd: scrollNextPage(); break; case QAbstractSlider::SliderPageStepSub: scrollPrevPage(); break; case QAbstractSlider::SliderToMinimum: top_home(); break; case QAbstractSlider::SliderToMaximum: bottom_end(); break; } } void KateViewInternal::scrollNextPage() { scrollViewLines(qMax(linesDisplayed() - 1, 0)); } void KateViewInternal::scrollPrevPage() { scrollViewLines(-qMax(linesDisplayed() - 1, 0)); } void KateViewInternal::scrollPrevLine() { scrollViewLines(-1); } void KateViewInternal::scrollNextLine() { scrollViewLines(1); } KTextEditor::Cursor KateViewInternal::maxStartPos(bool changed) { cache()->setAcceptDirtyLayouts(true); if (m_cachedMaxStartPos.line() == -1 || changed) { KTextEditor::Cursor end(m_view->textFolding().visibleLines() - 1, doc()->lineLength(m_view->textFolding().visibleLineToLine(m_view->textFolding().visibleLines() - 1))); if (m_view->config()->scrollPastEnd()) { m_cachedMaxStartPos = viewLineOffset(end, -m_minLinesVisible); } else { m_cachedMaxStartPos = viewLineOffset(end, -(linesDisplayed() - 1)); } } cache()->setAcceptDirtyLayouts(false); return m_cachedMaxStartPos; } // c is a virtual cursor void KateViewInternal::scrollPos(KTextEditor::Cursor &c, bool force, bool calledExternally, bool emitSignals) { if (!force && ((!m_view->dynWordWrap() && c.line() == startLine()) || c == startPos())) { return; } if (c.line() < 0) { c.setLine(0); } KTextEditor::Cursor limit = maxStartPos(); if (c > limit) { c = limit; // Re-check we're not just scrolling to the same place if (!force && ((!m_view->dynWordWrap() && c.line() == startLine()) || c == startPos())) { return; } } int viewLinesScrolled = 0; // only calculate if this is really used and useful, could be wrong here, please recheck // for larger scrolls this makes 2-4 seconds difference on my xeon with dyn. word wrap on // try to get it really working ;) bool viewLinesScrolledUsable = !force && (c.line() >= startLine() - linesDisplayed() - 1) && (c.line() <= endLine() + linesDisplayed() + 1); if (viewLinesScrolledUsable) { viewLinesScrolled = cache()->displayViewLine(c); } m_startPos.setPosition(c); // set false here but reversed if we return to makeVisible m_madeVisible = false; if (viewLinesScrolledUsable) { int lines = linesDisplayed(); if (m_view->textFolding().visibleLines() < lines) { KTextEditor::Cursor end(m_view->textFolding().visibleLines() - 1, doc()->lineLength(m_view->textFolding().visibleLineToLine(m_view->textFolding().visibleLines() - 1))); lines = qMin(linesDisplayed(), cache()->displayViewLine(end) + 1); } Q_ASSERT(lines >= 0); if (!calledExternally && qAbs(viewLinesScrolled) < lines && // NOTE: on some machines we must update if the floating widget is visible // otherwise strange painting bugs may occur during scrolling... !((m_view->m_floatTopMessageWidget && m_view->m_floatTopMessageWidget->isVisible()) || (m_view->m_floatBottomMessageWidget && m_view->m_floatBottomMessageWidget->isVisible())) ) { updateView(false, viewLinesScrolled); int scrollHeight = -(viewLinesScrolled * (int)renderer()->lineHeight()); // scroll excluding child widgets (floating notifications) scroll(0, scrollHeight, rect()); m_leftBorder->scroll(0, scrollHeight); if (emitSignals) { emit m_view->verticalScrollPositionChanged(m_view, c); emit m_view->displayRangeChanged(m_view); } return; } } updateView(); update(); m_leftBorder->update(); if (emitSignals) { emit m_view->verticalScrollPositionChanged(m_view, c); emit m_view->displayRangeChanged(m_view); } } void KateViewInternal::scrollColumns(int x) { if (x < 0) { x = 0; } if (x > m_columnScroll->maximum()) { x = m_columnScroll->maximum(); } if (x == m_startX) { return; } int dx = m_startX - x; m_startX = x; if (qAbs(dx) < width()) { // scroll excluding child widgets (floating notifications) scroll(dx, 0, rect()); } else { update(); } emit m_view->horizontalScrollPositionChanged(m_view); emit m_view->displayRangeChanged(m_view); bool blocked = m_columnScroll->blockSignals(true); m_columnScroll->setValue(m_startX); m_columnScroll->blockSignals(blocked); } // If changed is true, the lines that have been set dirty have been updated. void KateViewInternal::updateView(bool changed, int viewLinesScrolled) { doUpdateView(changed, viewLinesScrolled); if (changed) { updateDirty(); } } void KateViewInternal::doUpdateView(bool changed, int viewLinesScrolled) { if (!isVisible() && !viewLinesScrolled && !changed) { return; //When this view is not visible, don't do anything } bool blocked = m_lineScroll->blockSignals(true); if (width() != cache()->viewWidth()) { cache()->setViewWidth(width()); changed = true; } /* It was observed that height() could be negative here -- when the main Kate view has 0 as size (during creation), and there frame arount KateViewInternal. In which case we'd set the view cache to 0 (or less!) lines, and start allocating huge chunks of data, later. */ int newSize = (qMax(0, height()) / renderer()->lineHeight()) + 1; cache()->updateViewCache(startPos(), newSize, viewLinesScrolled); m_visibleLineCount = newSize; KTextEditor::Cursor maxStart = maxStartPos(changed); int maxLineScrollRange = maxStart.line(); if (m_view->dynWordWrap() && maxStart.column() != 0) { maxLineScrollRange++; } m_lineScroll->setRange(0, maxLineScrollRange); m_lineScroll->setValue(startPos().line()); m_lineScroll->setSingleStep(1); m_lineScroll->setPageStep(qMax(0, height()) / renderer()->lineHeight()); m_lineScroll->blockSignals(blocked); KateViewConfig::ScrollbarMode show_scrollbars = static_cast(view()->config()->showScrollbars()); bool visible = ((show_scrollbars == KateViewConfig::AlwaysOn) || ((show_scrollbars == KateViewConfig::ShowWhenNeeded) && (maxLineScrollRange != 0))); bool visible_dummy = visible; m_lineScroll->setVisible(visible); if (!m_view->dynWordWrap()) { int max = maxLen(startLine()) - width(); if (max < 0) { max = 0; } // if we lose the ability to scroll horizontally, move view to the far-left if (max == 0) { scrollColumns(0); } blocked = m_columnScroll->blockSignals(true); // disable scrollbar m_columnScroll->setDisabled(max == 0); visible = ((show_scrollbars == KateViewConfig::AlwaysOn) || ((show_scrollbars == KateViewConfig::ShowWhenNeeded) && (max != 0))); visible_dummy &= visible; m_columnScroll->setVisible(visible); m_columnScroll->setRange(0, max + (renderer()->spaceWidth() / 2)); // Add some space for the caret at EOL m_columnScroll->setValue(m_startX); // Approximate linescroll m_columnScroll->setSingleStep(renderer()->config()->fontMetrics().width(QLatin1Char('a'))); m_columnScroll->setPageStep(width()); m_columnScroll->blockSignals(blocked); } else { visible_dummy = false; } m_dummy->setVisible(visible_dummy); } /** * this function ensures a certain location is visible on the screen. * if endCol is -1, ignore making the columns visible. */ void KateViewInternal::makeVisible(const KTextEditor::Cursor &c, int endCol, bool force, bool center, bool calledExternally) { //qCDebug(LOG_KTE) << "MakeVisible start " << startPos() << " end " << endPos() << " -> request: " << c;// , new start [" << scroll.line << "," << scroll.col << "] lines " << (linesDisplayed() - 1) << " height " << height(); // if the line is in a folded region, unfold all the way up //if ( doc()->foldingTree()->findNodeForLine( c.line )->visible ) // qCDebug(LOG_KTE)<<"line ("< endPos())) { KTextEditor::Cursor scroll = viewLineOffset(c, -int(linesDisplayed()) / 2); scrollPos(scroll, false, calledExternally); } else if (c > viewLineOffset(startPos(), linesDisplayed() - m_minLinesVisible - 1)) { KTextEditor::Cursor scroll = viewLineOffset(c, -(linesDisplayed() - m_minLinesVisible - 1)); scrollPos(scroll, false, calledExternally); } else if (c < viewLineOffset(startPos(), m_minLinesVisible)) { KTextEditor::Cursor scroll = viewLineOffset(c, -m_minLinesVisible); scrollPos(scroll, false, calledExternally); } else { // Check to see that we're not showing blank lines KTextEditor::Cursor max = maxStartPos(); if (startPos() > max) { scrollPos(max, max.column(), calledExternally); } } if (!m_view->dynWordWrap() && (endCol != -1 || m_view->wrapCursor())) { KTextEditor::Cursor rc = toRealCursor(c); int sX = renderer()->cursorToX(cache()->textLayout(rc), rc, !m_view->wrapCursor()); int sXborder = sX - 8; if (sXborder < 0) { sXborder = 0; } if (sX < m_startX) { scrollColumns(sXborder); } else if (sX > m_startX + width()) { scrollColumns(sX - width() + 8); } } m_madeVisible = !force; #ifndef QT_NO_ACCESSIBILITY // FIXME -- is this needed? // QAccessible::updateAccessibility(this, KateCursorAccessible::ChildId, QAccessible::Focus); #endif } void KateViewInternal::slotRegionVisibilityChanged() { qCDebug(LOG_KTE); cache()->clear(); m_cachedMaxStartPos.setLine(-1); KTextEditor::Cursor max = maxStartPos(); if (startPos() > max) { scrollPos(max, false, false, false /* don't emit signals! */); } // if text was folded: make sure the cursor is on a visible line qint64 foldedRangeId = -1; if (!m_view->textFolding().isLineVisible(m_cursor.line(), &foldedRangeId)) { KTextEditor::Range foldingRange = m_view->textFolding().foldingRange(foldedRangeId); Q_ASSERT(foldingRange.start().isValid()); // set cursor to start of folding region updateCursor(foldingRange.start(), true); } else { // force an update of the cursor, since otherwise the m_displayCursor // line may be below the total amount of visible lines. updateCursor(m_cursor, true); } updateView(); update(); m_leftBorder->update(); // emit signals here, scrollPos has this disabled, to ensure we do this after all stuff is updated! emit m_view->verticalScrollPositionChanged(m_view, max); emit m_view->displayRangeChanged(m_view); } void KateViewInternal::slotRegionBeginEndAddedRemoved(unsigned int) { qCDebug(LOG_KTE); // FIXME: performance problem m_leftBorder->update(); } void KateViewInternal::showEvent(QShowEvent *e) { updateView(); QWidget::showEvent(e); } int KateViewInternal::linesDisplayed() const { int h = height(); // catch zero heights, even if should not happen int fh = qMax(1, renderer()->lineHeight()); // default to 1, there is always one line around.... // too many places calc with linesDisplayed() - 1 return qMax(1, (h - (h % fh)) / fh); } QPoint KateViewInternal::cursorToCoordinate(const KTextEditor::Cursor &cursor, bool realCursor, bool includeBorder) const { if (cursor.line() >= doc()->lines()) { return QPoint(-1, -1); } int viewLine = cache()->displayViewLine(realCursor ? toVirtualCursor(cursor) : cursor, true); if (viewLine < 0 || viewLine >= cache()->viewCacheLineCount()) { return QPoint(-1, -1); } const int y = (int)viewLine * renderer()->lineHeight(); KateTextLayout layout = cache()->viewLine(viewLine); if (cursor.column() > doc()->lineLength(cursor.line())) { return QPoint(-1, -1); } int x = 0; // only set x value if we have a valid layout (bug #171027) if (layout.isValid()) { x = (int)layout.lineLayout().cursorToX(cursor.column()); } // else // qCDebug(LOG_KTE) << "Invalid Layout"; if (includeBorder) { x += m_leftBorder->width(); } x -= startX(); return QPoint(x, y); } QPoint KateViewInternal::cursorCoordinates(bool includeBorder) const { return cursorToCoordinate(m_displayCursor, false, includeBorder); } KTextEditor::Cursor KateViewInternal::findMatchingBracket() { KTextEditor::Cursor c; if (!m_bm->toRange().isValid()) { return KTextEditor::Cursor::invalid(); } Q_ASSERT(m_bmEnd->toRange().isValid()); Q_ASSERT(m_bmStart->toRange().isValid()); if (m_bmStart->toRange().contains(m_cursor) || m_bmStart->end() == m_cursor.toCursor()) { c = m_bmEnd->end(); } else if (m_bmEnd->toRange().contains(m_cursor) || m_bmEnd->end() == m_cursor.toCursor()) { c = m_bmStart->start(); } else { // should never happen: a range exists, but the cursor position is // neither at the start nor at the end... return KTextEditor::Cursor::invalid(); } return c; } void KateViewInternal::doReturn() { doc()->newLine(m_view); m_leftBorder->updateForCursorLineChange(); updateView(); } void KateViewInternal::doSmartNewline() { int ln = m_cursor.line(); Kate::TextLine line = doc()->kateTextLine(ln); int col = qMin(m_cursor.column(), line->firstChar()); if (col != -1) { while (line->length() > col && !(line->at(col).isLetterOrNumber() || line->at(col) == QLatin1Char('_')) && col < m_cursor.column()) { ++col; } } else { col = line->length(); // stay indented } doc()->editStart(); doc()->editWrapLine(ln, m_cursor.column()); doc()->insertText(KTextEditor::Cursor(ln + 1, 0), line->string(0, col)); doc()->editEnd(); updateView(); } void KateViewInternal::doDelete() { doc()->del(m_view, m_cursor); } void KateViewInternal::doBackspace() { doc()->backspace(m_view, m_cursor); } void KateViewInternal::doTabulator() { doc()->insertTab(m_view, m_cursor); } void KateViewInternal::doTranspose() { doc()->transpose(m_cursor); } void KateViewInternal::doDeletePrevWord() { doc()->editStart(); wordPrev(true); KTextEditor::Range selection = m_view->selectionRange(); m_view->removeSelectedText(); doc()->editEnd(); tagRange(selection, true); updateDirty(); } void KateViewInternal::doDeleteNextWord() { doc()->editStart(); wordNext(true); KTextEditor::Range selection = m_view->selectionRange(); m_view->removeSelectedText(); doc()->editEnd(); tagRange(selection, true); updateDirty(); } class CalculatingCursor { public: // These constructors constrain their arguments to valid positions // before only the third one did, but that leads to crashs // see bug 227449 CalculatingCursor(KateViewInternal *vi) : m_vi(vi) { makeValid(); } CalculatingCursor(KateViewInternal *vi, const KTextEditor::Cursor &c) : m_cursor(c) , m_vi(vi) { makeValid(); } CalculatingCursor(KateViewInternal *vi, int line, int col) : m_cursor(line, col) , m_vi(vi) { makeValid(); } virtual ~CalculatingCursor() { } int line() const { return m_cursor.line(); } int column() const { return m_cursor.column(); } operator KTextEditor::Cursor() const { return m_cursor; } virtual CalculatingCursor &operator+=(int n) = 0; virtual CalculatingCursor &operator-=(int n) = 0; CalculatingCursor &operator++() { return operator+=(1); } CalculatingCursor &operator--() { return operator-=(1); } void makeValid() { m_cursor.setLine(qBound(0, line(), int(doc()->lines() - 1))); if (view()->wrapCursor()) { m_cursor.setColumn(qBound(0, column(), doc()->lineLength(line()))); } else { m_cursor.setColumn(qMax(0, column())); } Q_ASSERT(valid()); } void toEdge(KateViewInternal::Bias bias) { if (bias == KateViewInternal::left) { m_cursor.setColumn(0); } else if (bias == KateViewInternal::right) { m_cursor.setColumn(doc()->lineLength(line())); } } bool atEdge() const { return atEdge(KateViewInternal::left) || atEdge(KateViewInternal::right); } bool atEdge(KateViewInternal::Bias bias) const { switch (bias) { case KateViewInternal::left: return column() == 0; case KateViewInternal::none: return atEdge(); case KateViewInternal::right: return column() >= doc()->lineLength(line()); default: Q_ASSERT(false); return false; } } protected: bool valid() const { return line() >= 0 && line() < doc()->lines() && column() >= 0 && (!view()->wrapCursor() || column() <= doc()->lineLength(line())); } KTextEditor::ViewPrivate *view() { return m_vi->m_view; } const KTextEditor::ViewPrivate *view() const { return m_vi->m_view; } KTextEditor::DocumentPrivate *doc() { return view()->doc(); } const KTextEditor::DocumentPrivate *doc() const { return view()->doc(); } KTextEditor::Cursor m_cursor; KateViewInternal *m_vi; }; class BoundedCursor : public CalculatingCursor { public: BoundedCursor(KateViewInternal *vi) : CalculatingCursor(vi) {} BoundedCursor(KateViewInternal *vi, const KTextEditor::Cursor &c) : CalculatingCursor(vi, c) {} BoundedCursor(KateViewInternal *vi, int line, int col) : CalculatingCursor(vi, line, col) {} CalculatingCursor &operator+=(int n) Q_DECL_OVERRIDE { KateLineLayoutPtr thisLine = m_vi->cache()->line(line()); if (!thisLine->isValid()) { qCWarning(LOG_KTE) << "Did not retrieve valid layout for line " << line(); return *this; } const bool wrapCursor = view()->wrapCursor(); int maxColumn = -1; if (n >= 0) { for (int i = 0; i < n; i++) { if (column() >= thisLine->length()) { if (wrapCursor) { break; } else if (view()->dynWordWrap()) { // Don't go past the edge of the screen in dynamic wrapping mode if (maxColumn == -1) { maxColumn = thisLine->length() + ((m_vi->width() - thisLine->widthOfLastLine()) / m_vi->renderer()->spaceWidth()) - 1; } if (column() >= maxColumn) { m_cursor.setColumn(maxColumn); break; } m_cursor.setColumn(column() + 1); } else { m_cursor.setColumn(column() + 1); } } else { m_cursor.setColumn(thisLine->layout()->nextCursorPosition(column())); } } } else { for (int i = 0; i > n; i--) { if (column() >= thisLine->length()) { m_cursor.setColumn(column() - 1); } else if (column() == 0) { break; } else { m_cursor.setColumn(thisLine->layout()->previousCursorPosition(column())); } } } Q_ASSERT(valid()); return *this; } CalculatingCursor &operator-=(int n) Q_DECL_OVERRIDE { return operator+=(-n); } }; class WrappingCursor : public CalculatingCursor { public: WrappingCursor(KateViewInternal *vi) : CalculatingCursor(vi) {} WrappingCursor(KateViewInternal *vi, const KTextEditor::Cursor &c) : CalculatingCursor(vi, c) {} WrappingCursor(KateViewInternal *vi, int line, int col) : CalculatingCursor(vi, line, col) {} CalculatingCursor &operator+=(int n) Q_DECL_OVERRIDE { KateLineLayoutPtr thisLine = m_vi->cache()->line(line()); if (!thisLine->isValid()) { qCWarning(LOG_KTE) << "Did not retrieve a valid layout for line " << line(); return *this; } if (n >= 0) { for (int i = 0; i < n; i++) { if (column() >= thisLine->length()) { // Have come to the end of a line if (line() >= doc()->lines() - 1) // Have come to the end of the document { break; } // Advance to the beginning of the next line m_cursor.setColumn(0); m_cursor.setLine(line() + 1); // Retrieve the next text range thisLine = m_vi->cache()->line(line()); if (!thisLine->isValid()) { qCWarning(LOG_KTE) << "Did not retrieve a valid layout for line " << line(); return *this; } continue; } m_cursor.setColumn(thisLine->layout()->nextCursorPosition(column())); } } else { for (int i = 0; i > n; i--) { if (column() == 0) { // Have come to the start of the document if (line() == 0) { break; } // Start going back to the end of the last line m_cursor.setLine(line() - 1); // Retrieve the next text range thisLine = m_vi->cache()->line(line()); if (!thisLine->isValid()) { qCWarning(LOG_KTE) << "Did not retrieve a valid layout for line " << line(); return *this; } // Finish going back to the end of the last line m_cursor.setColumn(thisLine->length()); continue; } if (column() > thisLine->length()) { m_cursor.setColumn(column() - 1); } else { m_cursor.setColumn(thisLine->layout()->previousCursorPosition(column())); } } } Q_ASSERT(valid()); return *this; } CalculatingCursor &operator-=(int n) Q_DECL_OVERRIDE { return operator+=(-n); } }; void KateViewInternal::moveChar(KateViewInternal::Bias bias, bool sel) { KTextEditor::Cursor c; if (m_view->wrapCursor()) { c = WrappingCursor(this, m_cursor) += bias; } else { c = BoundedCursor(this, m_cursor) += bias; } updateSelection(c, sel); updateCursor(c); } void KateViewInternal::cursorPrevChar(bool sel) { if (! m_view->wrapCursor() && m_cursor.column() == 0) { return; } moveChar(KateViewInternal::left, sel); } void KateViewInternal::cursorNextChar(bool sel) { moveChar(KateViewInternal::right, sel); } void KateViewInternal::wordPrev(bool sel) { WrappingCursor c(this, m_cursor); // First we skip backwards all space. // Then we look up into which category the current position falls: // 1. a "word" character // 2. a "non-word" character (except space) // 3. the beginning of the line // and skip all preceding characters that fall into this class. // The code assumes that space is never part of the word character class. KateHighlighting *h = doc()->highlight(); if (!c.atEdge(left)) { while (!c.atEdge(left) && doc()->line(c.line())[ c.column() - 1 ].isSpace()) { --c; } } if (c.atEdge(left)) { --c; } else if (h->isInWord(doc()->line(c.line())[ c.column() - 1 ])) { while (!c.atEdge(left) && h->isInWord(doc()->line(c.line())[ c.column() - 1 ])) { --c; } } else { while (!c.atEdge(left) && !h->isInWord(doc()->line(c.line())[ c.column() - 1 ]) // in order to stay symmetric to wordLeft() // we must not skip space preceding a non-word sequence && !doc()->line(c.line())[ c.column() - 1 ].isSpace()) { --c; } } updateSelection(c, sel); updateCursor(c); } void KateViewInternal::wordNext(bool sel) { WrappingCursor c(this, m_cursor); // We look up into which category the current position falls: // 1. a "word" character // 2. a "non-word" character (except space) // 3. the end of the line // and skip all following characters that fall into this class. // If the skipped characters are followed by space, we skip that too. // The code assumes that space is never part of the word character class. KateHighlighting *h = doc()->highlight(); if (c.atEdge(right)) { ++c; } else if (h->isInWord(doc()->line(c.line())[ c.column() ])) { while (!c.atEdge(right) && h->isInWord(doc()->line(c.line())[ c.column() ])) { ++c; } } else { while (!c.atEdge(right) && !h->isInWord(doc()->line(c.line())[ c.column() ]) // we must not skip space, because if that space is followed // by more non-word characters, we would skip them, too && !doc()->line(c.line())[ c.column() ].isSpace()) { ++c; } } while (!c.atEdge(right) && doc()->line(c.line())[ c.column() ].isSpace()) { ++c; } updateSelection(c, sel); updateCursor(c); } void KateViewInternal::moveEdge(KateViewInternal::Bias bias, bool sel) { BoundedCursor c(this, m_cursor); c.toEdge(bias); updateSelection(c, sel); updateCursor(c); } void KateViewInternal::home(bool sel) { if (m_view->dynWordWrap() && currentLayout().startCol()) { // Allow us to go to the real start if we're already at the start of the view line if (m_cursor.column() != currentLayout().startCol()) { KTextEditor::Cursor c = currentLayout().start(); updateSelection(c, sel); updateCursor(c); return; } } if (!doc()->config()->smartHome()) { moveEdge(left, sel); return; } Kate::TextLine l = doc()->kateTextLine(m_cursor.line()); if (!l) { return; } KTextEditor::Cursor c = m_cursor; int lc = l->firstChar(); if (lc < 0 || c.column() == lc) { c.setColumn(0); } else { c.setColumn(lc); } updateSelection(c, sel); updateCursor(c, true); } void KateViewInternal::end(bool sel) { KateTextLayout layout = currentLayout(); if (m_view->dynWordWrap() && layout.wrap()) { // Allow us to go to the real end if we're already at the end of the view line if (m_cursor.column() < layout.endCol() - 1) { KTextEditor::Cursor c(m_cursor.line(), layout.endCol() - 1); updateSelection(c, sel); updateCursor(c); return; } } if (!doc()->config()->smartHome()) { moveEdge(right, sel); return; } Kate::TextLine l = doc()->kateTextLine(m_cursor.line()); if (!l) { return; } // "Smart End", as requested in bugs #78258 and #106970 if (m_cursor.column() == doc()->lineLength(m_cursor.line())) { KTextEditor::Cursor c = m_cursor; c.setColumn(l->lastChar() + 1); updateSelection(c, sel); updateCursor(c, true); } else { moveEdge(right, sel); } } KateTextLayout KateViewInternal::currentLayout() const { return cache()->textLayout(m_cursor); } KateTextLayout KateViewInternal::previousLayout() const { int currentViewLine = cache()->viewLine(m_cursor); if (currentViewLine) { return cache()->textLayout(m_cursor.line(), currentViewLine - 1); } else { return cache()->textLayout(m_view->textFolding().visibleLineToLine(m_displayCursor.line() - 1), -1); } } KateTextLayout KateViewInternal::nextLayout() const { int currentViewLine = cache()->viewLine(m_cursor) + 1; if (currentViewLine >= cache()->line(m_cursor.line())->viewLineCount()) { currentViewLine = 0; return cache()->textLayout(m_view->textFolding().visibleLineToLine(m_displayCursor.line() + 1), currentViewLine); } else { return cache()->textLayout(m_cursor.line(), currentViewLine); } } /* * This returns the cursor which is offset by (offset) view lines. * This is the main function which is called by code not specifically dealing with word-wrap. * The opposite conversion (cursor to offset) can be done with cache()->displayViewLine(). * * The cursors involved are virtual cursors (ie. equivalent to m_displayCursor) */ KTextEditor::Cursor KateViewInternal::viewLineOffset(const KTextEditor::Cursor &virtualCursor, int offset, bool keepX) { if (!m_view->dynWordWrap()) { KTextEditor::Cursor ret(qMin((int)m_view->textFolding().visibleLines() - 1, virtualCursor.line() + offset), 0); if (ret.line() < 0) { ret.setLine(0); } if (keepX) { int realLine = m_view->textFolding().visibleLineToLine(ret.line()); KateTextLayout t = cache()->textLayout(realLine, 0); Q_ASSERT(t.isValid()); ret.setColumn(renderer()->xToCursor(t, m_preservedX, !m_view->wrapCursor()).column()); } return ret; } KTextEditor::Cursor realCursor = virtualCursor; realCursor.setLine(m_view->textFolding().visibleLineToLine(m_view->textFolding().lineToVisibleLine(virtualCursor.line()))); int cursorViewLine = cache()->viewLine(realCursor); int currentOffset = 0; int virtualLine = 0; bool forwards = (offset > 0) ? true : false; if (forwards) { currentOffset = cache()->lastViewLine(realCursor.line()) - cursorViewLine; if (offset <= currentOffset) { // the answer is on the same line KateTextLayout thisLine = cache()->textLayout(realCursor.line(), cursorViewLine + offset); Q_ASSERT(thisLine.virtualLine() == (int) m_view->textFolding().lineToVisibleLine(virtualCursor.line())); return KTextEditor::Cursor(virtualCursor.line(), thisLine.startCol()); } virtualLine = virtualCursor.line() + 1; } else { offset = -offset; currentOffset = cursorViewLine; if (offset <= currentOffset) { // the answer is on the same line KateTextLayout thisLine = cache()->textLayout(realCursor.line(), cursorViewLine - offset); Q_ASSERT(thisLine.virtualLine() == (int) m_view->textFolding().lineToVisibleLine(virtualCursor.line())); return KTextEditor::Cursor(virtualCursor.line(), thisLine.startCol()); } virtualLine = virtualCursor.line() - 1; } currentOffset++; while (virtualLine >= 0 && virtualLine < (int)m_view->textFolding().visibleLines()) { int realLine = m_view->textFolding().visibleLineToLine(virtualLine); KateLineLayoutPtr thisLine = cache()->line(realLine, virtualLine); if (!thisLine) { break; } for (int i = 0; i < thisLine->viewLineCount(); ++i) { if (offset == currentOffset) { KateTextLayout thisViewLine = thisLine->viewLine(i); if (!forwards) { // We actually want it the other way around int requiredViewLine = cache()->lastViewLine(realLine) - thisViewLine.viewLine(); if (requiredViewLine != thisViewLine.viewLine()) { thisViewLine = thisLine->viewLine(requiredViewLine); } } KTextEditor::Cursor ret(virtualLine, thisViewLine.startCol()); // keep column position if (keepX) { KTextEditor::Cursor realCursor = toRealCursor(virtualCursor); KateTextLayout t = cache()->textLayout(realCursor); // renderer()->cursorToX(t, realCursor, !m_view->wrapCursor()); realCursor = renderer()->xToCursor(thisViewLine, m_preservedX, !m_view->wrapCursor()); ret.setColumn(realCursor.column()); } return ret; } currentOffset++; } if (forwards) { virtualLine++; } else { virtualLine--; } } // Looks like we were asked for something a bit exotic. // Return the max/min valid position. if (forwards) { return KTextEditor::Cursor(m_view->textFolding().visibleLines() - 1, doc()->lineLength(m_view->textFolding().visibleLineToLine(m_view->textFolding().visibleLines() - 1))); } else { return KTextEditor::Cursor(0, 0); } } int KateViewInternal::lineMaxCursorX(const KateTextLayout &range) { if (!m_view->wrapCursor() && !range.wrap()) { return INT_MAX; } int maxX = range.endX(); if (maxX && range.wrap()) { QChar lastCharInLine = doc()->kateTextLine(range.line())->at(range.endCol() - 1); maxX -= renderer()->config()->fontMetrics().width(lastCharInLine); } return maxX; } int KateViewInternal::lineMaxCol(const KateTextLayout &range) { int maxCol = range.endCol(); if (maxCol && range.wrap()) { maxCol--; } return maxCol; } void KateViewInternal::cursorUp(bool sel) { if (!sel && m_view->completionWidget()->isCompletionActive()) { m_view->completionWidget()->cursorUp(); return; } // assert that the display cursor is in visible lines Q_ASSERT(m_displayCursor.line() < m_view->textFolding().visibleLines()); /** * move cursor to start of line, if we are at first line! */ if (m_displayCursor.line() == 0 && (!m_view->dynWordWrap() || cache()->viewLine(m_cursor) == 0)) { home(sel); return; } m_preserveX = true; KateTextLayout thisLine = currentLayout(); // This is not the first line because that is already simplified out above KateTextLayout pRange = previousLayout(); // Ensure we're in the right spot Q_ASSERT(m_cursor.line() == thisLine.line()); Q_ASSERT(m_cursor.column() >= thisLine.startCol()); Q_ASSERT(!thisLine.wrap() || m_cursor.column() < thisLine.endCol()); KTextEditor::Cursor c = renderer()->xToCursor(pRange, m_preservedX, !m_view->wrapCursor()); updateSelection(c, sel); updateCursor(c); } void KateViewInternal::cursorDown(bool sel) { if (!sel && m_view->completionWidget()->isCompletionActive()) { m_view->completionWidget()->cursorDown(); return; } /** * move cursor to end of line, if we are at last line! */ if ((m_displayCursor.line() >= m_view->textFolding().visibleLines() - 1) && (!m_view->dynWordWrap() || cache()->viewLine(m_cursor) == cache()->lastViewLine(m_cursor.line()))) { end(sel); return; } m_preserveX = true; KateTextLayout thisLine = currentLayout(); // This is not the last line because that is already simplified out above KateTextLayout nRange = nextLayout(); // Ensure we're in the right spot Q_ASSERT((m_cursor.line() == thisLine.line()) && (m_cursor.column() >= thisLine.startCol()) && (!thisLine.wrap() || m_cursor.column() < thisLine.endCol())); KTextEditor::Cursor c = renderer()->xToCursor(nRange, m_preservedX, !m_view->wrapCursor()); updateSelection(c, sel); updateCursor(c); } void KateViewInternal::cursorToMatchingBracket(bool sel) { KTextEditor::Cursor c = findMatchingBracket(); if (c.isValid()) { updateSelection(c, sel); updateCursor(c); } } void KateViewInternal::topOfView(bool sel) { KTextEditor::Cursor c = viewLineOffset(startPos(), m_minLinesVisible); updateSelection(toRealCursor(c), sel); updateCursor(toRealCursor(c)); } void KateViewInternal::bottomOfView(bool sel) { KTextEditor::Cursor c = viewLineOffset(endPos(), -m_minLinesVisible); updateSelection(toRealCursor(c), sel); updateCursor(toRealCursor(c)); } // lines is the offset to scroll by void KateViewInternal::scrollLines(int lines, bool sel) { KTextEditor::Cursor c = viewLineOffset(m_displayCursor, lines, true); // Fix the virtual cursor -> real cursor c.setLine(m_view->textFolding().visibleLineToLine(c.line())); updateSelection(c, sel); updateCursor(c); } // This is a bit misleading... it's asking for the view to be scrolled, not the cursor void KateViewInternal::scrollUp() { KTextEditor::Cursor newPos = viewLineOffset(startPos(), -1); scrollPos(newPos); } void KateViewInternal::scrollDown() { KTextEditor::Cursor newPos = viewLineOffset(startPos(), 1); scrollPos(newPos); } void KateViewInternal::setAutoCenterLines(int viewLines, bool updateView) { m_autoCenterLines = viewLines; m_minLinesVisible = qMin(int((linesDisplayed() - 1) / 2), m_autoCenterLines); if (updateView) { KateViewInternal::updateView(); } } void KateViewInternal::pageUp(bool sel, bool half) { if (m_view->isCompletionActive()) { m_view->completionWidget()->pageUp(); return; } // remember the view line and x pos int viewLine = cache()->displayViewLine(m_displayCursor); bool atTop = startPos().atStartOfDocument(); // Adjust for an auto-centering cursor int lineadj = m_minLinesVisible; int linesToScroll; if (! half) { linesToScroll = -qMax((linesDisplayed() - 1) - lineadj, 0); } else { linesToScroll = -qMax((linesDisplayed() / 2 - 1) - lineadj, 0); } m_preserveX = true; if (!doc()->pageUpDownMovesCursor() && !atTop) { KTextEditor::Cursor newStartPos = viewLineOffset(startPos(), linesToScroll - 1); scrollPos(newStartPos); // put the cursor back approximately where it was KTextEditor::Cursor newPos = toRealCursor(viewLineOffset(newStartPos, viewLine, true)); KateTextLayout newLine = cache()->textLayout(newPos); newPos = renderer()->xToCursor(newLine, m_preservedX, !m_view->wrapCursor()); m_preserveX = true; updateSelection(newPos, sel); updateCursor(newPos); } else { scrollLines(linesToScroll, sel); } } void KateViewInternal::pageDown(bool sel, bool half) { if (m_view->isCompletionActive()) { m_view->completionWidget()->pageDown(); return; } // remember the view line int viewLine = cache()->displayViewLine(m_displayCursor); bool atEnd = startPos() >= m_cachedMaxStartPos; // Adjust for an auto-centering cursor int lineadj = m_minLinesVisible; int linesToScroll; if (! half) { linesToScroll = qMax((linesDisplayed() - 1) - lineadj, 0); } else { linesToScroll = qMax((linesDisplayed() / 2 - 1) - lineadj, 0); } m_preserveX = true; if (!doc()->pageUpDownMovesCursor() && !atEnd) { KTextEditor::Cursor newStartPos = viewLineOffset(startPos(), linesToScroll + 1); scrollPos(newStartPos); // put the cursor back approximately where it was KTextEditor::Cursor newPos = toRealCursor(viewLineOffset(newStartPos, viewLine, true)); KateTextLayout newLine = cache()->textLayout(newPos); newPos = renderer()->xToCursor(newLine, m_preservedX, !m_view->wrapCursor()); m_preserveX = true; updateSelection(newPos, sel); updateCursor(newPos); } else { scrollLines(linesToScroll, sel); } } int KateViewInternal::maxLen(int startLine) { Q_ASSERT(!m_view->dynWordWrap()); int displayLines = (m_view->height() / renderer()->lineHeight()) + 1; int maxLen = 0; for (int z = 0; z < displayLines; z++) { int virtualLine = startLine + z; if (virtualLine < 0 || virtualLine >= (int)m_view->textFolding().visibleLines()) { break; } maxLen = qMax(maxLen, cache()->line(m_view->textFolding().visibleLineToLine(virtualLine))->width()); } return maxLen; } bool KateViewInternal::columnScrollingPossible() { return !m_view->dynWordWrap() && m_columnScroll->isEnabled() && (m_columnScroll->maximum() > 0); } bool KateViewInternal::lineScrollingPossible() { return m_lineScroll->minimum() != m_lineScroll->maximum(); } void KateViewInternal::top(bool sel) { KTextEditor::Cursor newCursor(0, 0); newCursor = renderer()->xToCursor(cache()->textLayout(newCursor), m_preservedX, !m_view->wrapCursor()); updateSelection(newCursor, sel); updateCursor(newCursor); } void KateViewInternal::bottom(bool sel) { KTextEditor::Cursor newCursor(doc()->lastLine(), 0); newCursor = renderer()->xToCursor(cache()->textLayout(newCursor), m_preservedX, !m_view->wrapCursor()); updateSelection(newCursor, sel); updateCursor(newCursor); } void KateViewInternal::top_home(bool sel) { if (m_view->isCompletionActive()) { m_view->completionWidget()->top(); return; } KTextEditor::Cursor c(0, 0); updateSelection(c, sel); updateCursor(c); } void KateViewInternal::bottom_end(bool sel) { if (m_view->isCompletionActive()) { m_view->completionWidget()->bottom(); return; } KTextEditor::Cursor c(doc()->lastLine(), doc()->lineLength(doc()->lastLine())); updateSelection(c, sel); updateCursor(c); } void KateViewInternal::updateSelection(const KTextEditor::Cursor &_newCursor, bool keepSel) { KTextEditor::Cursor newCursor = _newCursor; if (keepSel) { if (!m_view->selection() || (m_selectAnchor.line() == -1) //don't kill the selection if we have a persistent selection and //the cursor is inside or at the boundaries of the selected area || (m_view->config()->persistentSelection() && !(m_view->selectionRange().contains(m_cursor) || m_view->selectionRange().boundaryAtCursor(m_cursor)))) { m_selectAnchor = m_cursor; setSelection(KTextEditor::Range(m_cursor, newCursor)); } else { bool doSelect = true; switch (m_selectionMode) { case Word: { // Restore selStartCached if needed. It gets nuked by // viewSelectionChanged if we drag the selection into non-existence, // which can legitimately happen if a shift+DC selection is unable to // set a "proper" (i.e. non-empty) cached selection, e.g. because the // start was on something that isn't a word. Word select mode relies // on the cached selection being set properly, even if it is empty // (i.e. selStartCached == selEndCached). if (!m_selectionCached.isValid()) { m_selectionCached.setStart(m_selectionCached.end()); } int c; if (newCursor > m_selectionCached.start()) { m_selectAnchor = m_selectionCached.start(); Kate::TextLine l = doc()->kateTextLine(newCursor.line()); c = newCursor.column(); if (c > 0 && doc()->highlight()->isInWord(l->at(c - 1))) { for (; c < l->length(); c++) if (!doc()->highlight()->isInWord(l->at(c))) { break; } } newCursor.setColumn(c); } else if (newCursor < m_selectionCached.start()) { m_selectAnchor = m_selectionCached.end(); Kate::TextLine l = doc()->kateTextLine(newCursor.line()); c = newCursor.column(); if (c > 0 && c < doc()->lineLength(newCursor.line()) && doc()->highlight()->isInWord(l->at(c)) && doc()->highlight()->isInWord(l->at(c - 1))) { for (c -= 2; c >= 0; c--) if (!doc()->highlight()->isInWord(l->at(c))) { break; } newCursor.setColumn(c + 1); } } else { doSelect = false; } } break; case Line: if (!m_selectionCached.isValid()) { m_selectionCached = KTextEditor::Range(endLine(), 0, endLine(), 0); } if (newCursor.line() > m_selectionCached.start().line()) { if (newCursor.line() + 1 >= doc()->lines()) { newCursor.setColumn(doc()->line(newCursor.line()).length()); } else { newCursor.setPosition(newCursor.line() + 1, 0); } // Grow to include the entire line m_selectAnchor = m_selectionCached.start(); m_selectAnchor.setColumn(0); } else if (newCursor.line() < m_selectionCached.start().line()) { newCursor.setColumn(0); // Grow to include entire line m_selectAnchor = m_selectionCached.end(); if (m_selectAnchor.column() > 0) { if (m_selectAnchor.line() + 1 >= doc()->lines()) { m_selectAnchor.setColumn(doc()->line(newCursor.line()).length()); } else { m_selectAnchor.setPosition(m_selectAnchor.line() + 1, 0); } } } else { // same line, ignore doSelect = false; } break; case Mouse: { if (!m_selectionCached.isValid()) { break; } if (newCursor > m_selectionCached.end()) { m_selectAnchor = m_selectionCached.start(); } else if (newCursor < m_selectionCached.start()) { m_selectAnchor = m_selectionCached.end(); } else { doSelect = false; } } break; default: /* nothing special to do */; } if (doSelect) { setSelection(KTextEditor::Range(m_selectAnchor, newCursor)); } else if (m_selectionCached.isValid()) { // we have a cached selection, so we restore that setSelection(m_selectionCached); } } m_selChangedByUser = true; } else if (!m_view->config()->persistentSelection()) { m_view->clearSelection(); m_selectionCached = KTextEditor::Range::invalid(); m_selectAnchor = KTextEditor::Cursor::invalid(); } #ifndef QT_NO_ACCESSIBILITY // FIXME KF5 // QAccessibleTextSelectionEvent ev(this, /* selection start, selection end*/); // QAccessible::updateAccessibility(&ev); #endif } void KateViewInternal::setSelection(const KTextEditor::Range &range) { disconnect(m_view, SIGNAL(selectionChanged(KTextEditor::View*)), this, SLOT(viewSelectionChanged())); m_view->setSelection(range); connect(m_view, SIGNAL(selectionChanged(KTextEditor::View*)), this, SLOT(viewSelectionChanged())); } void KateViewInternal::moveCursorToSelectionEdge() { if (!m_view->selection()) { return; } int tmp = m_minLinesVisible; m_minLinesVisible = 0; if (m_view->selectionRange().start() < m_selectAnchor) { updateCursor(m_view->selectionRange().start()); } else { updateCursor(m_view->selectionRange().end()); } m_minLinesVisible = tmp; } void KateViewInternal::updateCursor(const KTextEditor::Cursor &newCursor, bool force, bool center, bool calledExternally) { if (!force && (m_cursor.toCursor() == newCursor)) { m_displayCursor = toVirtualCursor(newCursor); if (!m_madeVisible && m_view == doc()->activeView()) { // unfold if required m_view->textFolding().ensureLineIsVisible(newCursor.line()); makeVisible(m_displayCursor, m_displayCursor.column(), false, center, calledExternally); } return; } if (m_cursor.line() != newCursor.line()) { m_leftBorder->updateForCursorLineChange(); } // unfold if required m_view->textFolding().ensureLineIsVisible(newCursor.line()); KTextEditor::Cursor oldDisplayCursor = m_displayCursor; m_displayCursor = toVirtualCursor(newCursor); m_cursor.setPosition(newCursor); if (m_view == doc()->activeView()) { makeVisible(m_displayCursor, m_displayCursor.column(), false, center, calledExternally); } updateBracketMarks(); // It's efficient enough to just tag them both without checking to see if they're on the same view line /* kdDebug()<<"oldDisplayCursor:"< 0) { m_cursorTimer.start(QApplication::cursorFlashTime() / 2); } renderer()->setDrawCaret(true); } // Remember the maximum X position if requested if (m_preserveX) { m_preserveX = false; } else { m_preservedX = renderer()->cursorToX(cache()->textLayout(m_cursor), m_cursor, !m_view->wrapCursor()); } //qCDebug(LOG_KTE) << "m_preservedX: " << m_preservedX << " (was "<< oldmaxx << "), m_cursorX: " << m_cursorX; //qCDebug(LOG_KTE) << "Cursor now located at real " << cursor.line << "," << cursor.col << ", virtual " << m_displayCursor.line << ", " << m_displayCursor.col << "; Top is " << startLine() << ", " << startPos().col; cursorMoved(); updateDirty(); //paintText(0, 0, width(), height(), true); emit m_view->cursorPositionChanged(m_view, m_cursor); } void KateViewInternal::updateBracketMarkAttributes() { KTextEditor::Attribute::Ptr bracketFill = KTextEditor::Attribute::Ptr(new KTextEditor::Attribute()); bracketFill->setBackground(m_view->m_renderer->config()->highlightedBracketColor()); bracketFill->setBackgroundFillWhitespace(false); if (QFontInfo(renderer()->currentFont()).fixedPitch()) { // make font bold only for fixed fonts, otherwise text jumps around bracketFill->setFontBold(); } m_bmStart->setAttribute(bracketFill); m_bmEnd->setAttribute(bracketFill); if (m_view->m_renderer->config()->showWholeBracketExpression()) { KTextEditor::Attribute::Ptr expressionFill = KTextEditor::Attribute::Ptr(new KTextEditor::Attribute()); expressionFill->setBackground(m_view->m_renderer->config()->highlightedBracketColor()); expressionFill->setBackgroundFillWhitespace(false); m_bm->setAttribute(expressionFill); } else { m_bm->setAttribute(KTextEditor::Attribute::Ptr(new KTextEditor::Attribute())); } } void KateViewInternal::updateBracketMarks() { // add some limit to this, this is really endless on big files without limit const int maxLines = 5000; const KTextEditor::Range newRange = doc()->findMatchingBracket(m_cursor, maxLines); // new range valid, then set ranges to it if (newRange.isValid()) { if (m_bm->toRange() == newRange) { return; } // modify full range m_bm->setRange(newRange); // modify start and end ranges m_bmStart->setRange(KTextEditor::Range(m_bm->start(), KTextEditor::Cursor(m_bm->start().line(), m_bm->start().column() + 1))); m_bmEnd->setRange(KTextEditor::Range(m_bm->end(), KTextEditor::Cursor(m_bm->end().line(), m_bm->end().column() + 1))); // flash matching bracket if (!renderer()->config()->animateBracketMatching()) { return; } const KTextEditor::Cursor flashPos = (m_cursor == m_bmStart->start() || m_cursor == m_bmStart->end()) ? m_bmEnd->start() : m_bm->start(); if (flashPos != m_bmLastFlashPos->toCursor()) { m_bmLastFlashPos->setPosition(flashPos); KTextEditor::Attribute::Ptr attribute = doc()->attributeAt(flashPos); attribute->setBackground(m_view->m_renderer->config()->highlightedBracketColor()); attribute->setFontBold(m_bmStart->attribute()->fontBold()); flashChar(flashPos, attribute); } return; } // new range was invalid m_bm->setRange(KTextEditor::Range::invalid()); m_bmStart->setRange(KTextEditor::Range::invalid()); m_bmEnd->setRange(KTextEditor::Range::invalid()); m_bmLastFlashPos->setPosition(KTextEditor::Cursor::invalid()); } bool KateViewInternal::tagLine(const KTextEditor::Cursor &virtualCursor) { // FIXME may be a more efficient way for this if ((int)m_view->textFolding().visibleLineToLine(virtualCursor.line()) > doc()->lastLine()) { return false; } // End FIXME int viewLine = cache()->displayViewLine(virtualCursor, true); if (viewLine >= 0 && viewLine < cache()->viewCacheLineCount()) { cache()->viewLine(viewLine).setDirty(); // tag one line more because of overlapping things like _, bug 335079 if (viewLine+1 < cache()->viewCacheLineCount()) { cache()->viewLine(viewLine+1).setDirty(); } m_leftBorder->update(0, lineToY(viewLine), m_leftBorder->width(), renderer()->lineHeight()); return true; } return false; } bool KateViewInternal::tagLines(int start, int end, bool realLines) { return tagLines(KTextEditor::Cursor(start, 0), KTextEditor::Cursor(end, -1), realLines); } bool KateViewInternal::tagLines(KTextEditor::Cursor start, KTextEditor::Cursor end, bool realCursors) { if (realCursors) { cache()->relayoutLines(start.line(), end.line()); //qCDebug(LOG_KTE)<<"realLines is true"; start = toVirtualCursor(start); end = toVirtualCursor(end); } else { cache()->relayoutLines(toRealCursor(start).line(), toRealCursor(end).line()); } if (end.line() < startLine()) { //qCDebug(LOG_KTE)<<"end endLine(), but cache may not be valid when checking, so use a // less optimal but still adequate approximation (potential overestimation but minimal performance difference) if (start.line() > startLine() + cache()->viewCacheLineCount()) { //qCDebug(LOG_KTE)<<"start> endLine"<updateViewCache(startPos()); //qCDebug(LOG_KTE) << "tagLines( [" << start << "], [" << end << "] )"; bool ret = false; for (int z = 0; z < cache()->viewCacheLineCount(); z++) { KateTextLayout &line = cache()->viewLine(z); if ((line.virtualLine() > start.line() || (line.virtualLine() == start.line() && line.endCol() >= start.column() && start.column() != -1)) && (line.virtualLine() < end.line() || (line.virtualLine() == end.line() && (line.startCol() <= end.column() || end.column() == -1)))) { ret = true; break; //qCDebug(LOG_KTE) << "Tagged line " << line.line(); } } if (!m_view->dynWordWrap()) { int y = lineToY(start.line()); // FIXME is this enough for when multiple lines are deleted int h = (end.line() - start.line() + 2) * renderer()->lineHeight(); if (end.line() >= m_view->textFolding().visibleLines() - 1) { h = height(); } m_leftBorder->update(0, y, m_leftBorder->width(), h); } else { // FIXME Do we get enough good info in editRemoveText to optimize this more? //bool justTagged = false; for (int z = 0; z < cache()->viewCacheLineCount(); z++) { KateTextLayout &line = cache()->viewLine(z); if (!line.isValid() || ((line.virtualLine() > start.line() || (line.virtualLine() == start.line() && line.endCol() >= start.column() && start.column() != -1)) && (line.virtualLine() < end.line() || (line.virtualLine() == end.line() && (line.startCol() <= end.column() || end.column() == -1))))) { //justTagged = true; m_leftBorder->update(0, z * renderer()->lineHeight(), m_leftBorder->width(), m_leftBorder->height()); break; } /*else if (justTagged) { justTagged = false; leftBorder->update (0, z * doc()->viewFont.fontHeight, leftBorder->width(), doc()->viewFont.fontHeight); break; }*/ } } return ret; } bool KateViewInternal::tagRange(const KTextEditor::Range &range, bool realCursors) { return tagLines(range.start(), range.end(), realCursors); } void KateViewInternal::tagAll() { // clear the cache... cache()->clear(); m_leftBorder->updateFont(); m_leftBorder->update(); } void KateViewInternal::paintCursor() { if (tagLine(m_displayCursor)) { updateDirty(); //paintText (0,0,width(), height(), true); } } // Point in content coordinates void KateViewInternal::placeCursor(const QPoint &p, bool keepSelection, bool updateSelection) { KateTextLayout thisLine = yToKateTextLayout(p.y()); KTextEditor::Cursor c; if (!thisLine.isValid()) { // probably user clicked below the last line -> use the last line thisLine = cache()->textLayout(doc()->lines() - 1, -1); } c = renderer()->xToCursor(thisLine, startX() + p.x(), !m_view->wrapCursor()); if (c.line() < 0 || c.line() >= doc()->lines()) { return; } if (updateSelection) { KateViewInternal::updateSelection(c, keepSelection); } int tmp = m_minLinesVisible; m_minLinesVisible = 0; updateCursor(c); m_minLinesVisible = tmp; if (updateSelection && keepSelection) { moveCursorToSelectionEdge(); } } // Point in content coordinates bool KateViewInternal::isTargetSelected(const QPoint &p) { const KateTextLayout &thisLine = yToKateTextLayout(p.y()); if (!thisLine.isValid()) { return false; } return m_view->cursorSelected(renderer()->xToCursor(thisLine, startX() + p.x(), !m_view->wrapCursor())); } //BEGIN EVENT HANDLING STUFF bool KateViewInternal::eventFilter(QObject *obj, QEvent *e) { switch (e->type()) { case QEvent::ChildAdded: case QEvent::ChildRemoved: { QChildEvent *c = static_cast(e); if (c->added()) { c->child()->installEventFilter(this); /*foreach (QWidget* child, c->child()->findChildren()) child->installEventFilter(this);*/ } else if (c->removed()) { c->child()->removeEventFilter(this); /*foreach (QWidget* child, c->child()->findChildren()) child->removeEventFilter(this);*/ } } break; case QEvent::ShortcutOverride: { QKeyEvent *k = static_cast(e); if (k->key() == Qt::Key_Escape && k->modifiers() == Qt::NoModifier) { if (m_view->isCompletionActive()) { m_view->abortCompletion(); k->accept(); //qCDebug(LOG_KTE) << obj << "shortcut override" << k->key() << "aborting completion"; return true; } else if (!m_view->bottomViewBar()->hiddenOrPermanent()) { m_view->bottomViewBar()->hideCurrentBarWidget(); k->accept(); //qCDebug(LOG_KTE) << obj << "shortcut override" << k->key() << "closing view bar"; return true; } else if (!m_view->config()->persistentSelection() && m_view->selection()) { m_currentInputMode->clearSelection(); k->accept(); //qCDebug(LOG_KTE) << obj << "shortcut override" << k->key() << "clearing selection"; return true; } } if (m_currentInputMode->stealKey(k)) { k->accept(); return true; } } break; case QEvent::KeyPress: { QKeyEvent *k = static_cast(e); // Override all other single key shortcuts which do not use a modifier other than Shift if (obj == this && (!k->modifiers() || k->modifiers() == Qt::ShiftModifier)) { keyPressEvent(k); if (k->isAccepted()) { //qCDebug(LOG_KTE) << obj << "shortcut override" << k->key() << "using keystroke"; return true; } } //qCDebug(LOG_KTE) << obj << "shortcut override" << k->key() << "ignoring"; } break; case QEvent::DragMove: { QPoint currentPoint = ((QDragMoveEvent *) e)->pos(); QRect doNotScrollRegion(s_scrollMargin, s_scrollMargin, width() - s_scrollMargin * 2, height() - s_scrollMargin * 2); if (!doNotScrollRegion.contains(currentPoint)) { startDragScroll(); // Keep sending move events ((QDragMoveEvent *)e)->accept(QRect(0, 0, 0, 0)); } dragMoveEvent((QDragMoveEvent *)e); } break; case QEvent::DragLeave: // happens only when pressing ESC while dragging stopDragScroll(); break; default: break; } return QWidget::eventFilter(obj, e); } void KateViewInternal::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Left && e->modifiers() == Qt::AltModifier) { m_view->emitNavigateLeft(); e->setAccepted(true); return; } if (e->key() == Qt::Key_Right && e->modifiers() == Qt::AltModifier) { m_view->emitNavigateRight(); e->setAccepted(true); return; } if (e->key() == Qt::Key_Up && e->modifiers() == Qt::AltModifier) { m_view->emitNavigateUp(); e->setAccepted(true); return; } if (e->key() == Qt::Key_Down && e->modifiers() == Qt::AltModifier) { m_view->emitNavigateDown(); e->setAccepted(true); return; } if (e->key() == Qt::Key_Return && e->modifiers() == Qt::AltModifier) { m_view->emitNavigateAccept(); e->setAccepted(true); return; } if (e->key() == Qt::Key_Backspace && e->modifiers() == Qt::AltModifier) { m_view->emitNavigateBack(); e->setAccepted(true); return; } if (e->key() == Qt::Key_Alt && m_view->completionWidget()->isCompletionActive()) { m_completionItemExpanded = m_view->completionWidget()->toggleExpanded(true); m_view->completionWidget()->resetHadNavigation(); m_altDownTime.start(); } // Note: AND'ing with is a quick hack to fix Key_Enter const int key = e->key() | (e->modifiers() & Qt::ShiftModifier); if (m_currentInputMode->keyPress(e)) { return; } if (!doc()->isReadWrite()) { e->ignore(); return; } if ((key == Qt::Key_Return) || (key == Qt::Key_Enter) || (key == Qt::SHIFT + Qt::Key_Return) || (key == Qt::SHIFT + Qt::Key_Enter)) { doReturn(); e->accept(); return; } if (key == Qt::Key_Backspace || key == Qt::SHIFT + Qt::Key_Backspace) { //m_view->backspace(); e->accept(); return; } if (key == Qt::Key_Tab || key == Qt::SHIFT + Qt::Key_Backtab || key == Qt::Key_Backtab) { if (m_view->completionWidget()->isCompletionActive()) { e->accept(); m_view->completionWidget()->tab(key != Qt::Key_Tab); return; } if (key == Qt::Key_Tab) { uint tabHandling = doc()->config()->tabHandling(); // convert tabSmart into tabInsertsTab or tabIndents: if (tabHandling == KateDocumentConfig::tabSmart) { // multiple lines selected if (m_view->selection() && !m_view->selectionRange().onSingleLine()) { tabHandling = KateDocumentConfig::tabIndents; } // otherwise: take look at cursor position else { // if the cursor is at or before the first non-space character // or on an empty line, // Tab indents, otherwise it inserts a tab character. Kate::TextLine line = doc()->kateTextLine(m_cursor.line()); int first = line->firstChar(); if (first < 0 || m_cursor.column() <= first) { tabHandling = KateDocumentConfig::tabIndents; } else { tabHandling = KateDocumentConfig::tabInsertsTab; } } } if (tabHandling == KateDocumentConfig::tabInsertsTab) { doc()->typeChars(m_view, QStringLiteral("\t")); } else { doc()->indent(m_view->selection() ? m_view->selectionRange() : KTextEditor::Range(m_cursor.line(), 0, m_cursor.line(), 0), 1); } e->accept(); return; } else if (doc()->config()->tabHandling() != KateDocumentConfig::tabInsertsTab) { // key == Qt::SHIFT+Qt::Key_Backtab || key == Qt::Key_Backtab doc()->indent(m_view->selection() ? m_view->selectionRange() : KTextEditor::Range(m_cursor.line(), 0, m_cursor.line(), 0), -1); e->accept(); return; } } if (!(e->modifiers() & Qt::ControlModifier) && !e->text().isEmpty() && doc()->typeChars(m_view, e->text())) { e->accept(); return; } // allow composition of AltGr + (q|2|3) on windows static const int altGR = Qt::ControlModifier | Qt::AltModifier; if ((e->modifiers() & altGR) == altGR && !e->text().isEmpty() && doc()->typeChars(m_view, e->text())) { e->accept(); return; } e->ignore(); } void KateViewInternal::keyReleaseEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Alt && m_view->completionWidget()->isCompletionActive() && ((m_completionItemExpanded && (m_view->completionWidget()->hadNavigation() || m_altDownTime.elapsed() > 300)) || (!m_completionItemExpanded && !m_view->completionWidget()->hadNavigation()))) { m_view->completionWidget()->toggleExpanded(false, true); } if ((e->modifiers() & Qt::SHIFT) == Qt::SHIFT) { m_shiftKeyPressed = true; } else { if (m_shiftKeyPressed) { m_shiftKeyPressed = false; if (m_selChangedByUser) { if (m_view->selection()) { QApplication::clipboard()->setText(m_view->selectionText(), QClipboard::Selection); } m_selChangedByUser = false; } } } e->ignore(); return; } void KateViewInternal::contextMenuEvent(QContextMenuEvent *e) { // try to show popup menu QPoint p = e->pos(); if (e->reason() == QContextMenuEvent::Keyboard) { makeVisible(m_displayCursor, 0); p = cursorCoordinates(false); p.rx() -= startX(); } else if (! m_view->selection() || m_view->config()->persistentSelection()) { placeCursor(e->pos()); } // popup is a qguardedptr now if (m_view->contextMenu()) { m_view->spellingMenu()->setUseMouseForMisspelledRange((e->reason() == QContextMenuEvent::Mouse)); m_view->contextMenu()->popup(mapToGlobal(p)); e->accept(); } } void KateViewInternal::mousePressEvent(QMouseEvent *e) { switch (e->button()) { case Qt::LeftButton: m_selChangedByUser = false; if (m_possibleTripleClick) { m_possibleTripleClick = false; m_selectionMode = Line; if (e->modifiers() & Qt::ShiftModifier) { updateSelection(m_cursor, true); } else { m_view->selectLine(m_cursor); if (m_view->selection()) { m_selectAnchor = m_view->selectionRange().start(); } } if (m_view->selection()) { QApplication::clipboard()->setText(m_view->selectionText(), QClipboard::Selection); } // Keep the line at the select anchor selected during further // mouse selection if (m_selectAnchor.line() > m_view->selectionRange().start().line()) { // Preserve the last selected line if (m_selectAnchor == m_view->selectionRange().end() && m_selectAnchor.column() == 0) { m_selectionCached.setStart(KTextEditor::Cursor(m_selectAnchor.line() - 1, 0)); } else { m_selectionCached.setStart(KTextEditor::Cursor(m_selectAnchor.line(), 0)); } m_selectionCached.setEnd(m_view->selectionRange().end()); } else { // Preserve the first selected line m_selectionCached.setStart(m_view->selectionRange().start()); if (m_view->selectionRange().end().line() > m_view->selectionRange().start().line()) { m_selectionCached.setEnd(KTextEditor::Cursor(m_view->selectionRange().start().line() + 1, 0)); } else { m_selectionCached.setEnd(m_view->selectionRange().end()); } } moveCursorToSelectionEdge(); m_scrollX = 0; m_scrollY = 0; m_scrollTimer.start(50); e->accept(); return; } else if (m_selectionMode == Default) { m_selectionMode = Mouse; } // request the software keyboard, if any if (e->button() == Qt::LeftButton && qApp->autoSipEnabled()) { QStyle::RequestSoftwareInputPanel behavior = QStyle::RequestSoftwareInputPanel(style()->styleHint(QStyle::SH_RequestSoftwareInputPanel)); if (hasFocus() || behavior == QStyle::RSIP_OnMouseClick) { QEvent event(QEvent::RequestSoftwareInputPanel); QApplication::sendEvent(this, &event); } } if (e->modifiers() & Qt::ShiftModifier) { if (!m_selectAnchor.isValid()) { m_selectAnchor = m_cursor; } } else { m_selectionCached = KTextEditor::Range::invalid(); } if (!(e->modifiers() & Qt::ShiftModifier) && isTargetSelected(e->pos())) { m_dragInfo.state = diPending; m_dragInfo.start = e->pos(); } else { m_dragInfo.state = diNone; if (e->modifiers() & Qt::ShiftModifier) { placeCursor(e->pos(), true, false); if (m_selectionCached.start().isValid()) { if (m_cursor.toCursor() < m_selectionCached.start()) { m_selectAnchor = m_selectionCached.end(); } else { m_selectAnchor = m_selectionCached.start(); } } setSelection(KTextEditor::Range(m_selectAnchor, m_cursor)); } else { placeCursor(e->pos()); } m_scrollX = 0; m_scrollY = 0; m_scrollTimer.start(50); } e->accept(); break; default: e->ignore(); break; } } void KateViewInternal::mouseDoubleClickEvent(QMouseEvent *e) { switch (e->button()) { case Qt::LeftButton: m_selectionMode = Word; if (e->modifiers() & Qt::ShiftModifier) { // Now select the word under the select anchor int cs, ce; Kate::TextLine l = doc()->kateTextLine(m_selectAnchor.line()); ce = m_selectAnchor.column(); if (ce > 0 && doc()->highlight()->isInWord(l->at(ce))) { for (; ce < l->length(); ce++) if (!doc()->highlight()->isInWord(l->at(ce))) { break; } } cs = m_selectAnchor.column() - 1; if (cs < doc()->lineLength(m_selectAnchor.line()) && doc()->highlight()->isInWord(l->at(cs))) { for (cs--; cs >= 0; cs--) if (!doc()->highlight()->isInWord(l->at(cs))) { break; } } // ...and keep it selected if (cs + 1 < ce) { m_selectionCached.setStart(KTextEditor::Cursor(m_selectAnchor.line(), cs + 1)); m_selectionCached.setEnd(KTextEditor::Cursor(m_selectAnchor.line(), ce)); } else { m_selectionCached.setStart(m_selectAnchor); m_selectionCached.setEnd(m_selectAnchor); } // Now word select to the mouse cursor placeCursor(e->pos(), true); } else { // first clear the selection, otherwise we run into bug #106402 // ...and set the cursor position, for the same reason (otherwise there // are *other* idiosyncrasies we can't fix without reintroducing said // bug) // Parameters: don't redraw, and don't emit selectionChanged signal yet m_view->clearSelection(false, false); placeCursor(e->pos()); m_view->selectWord(m_cursor); cursorToMatchingBracket(true); if (m_view->selection()) { m_selectAnchor = m_view->selectionRange().start(); m_selectionCached = m_view->selectionRange(); } else { m_selectAnchor = m_cursor; m_selectionCached = KTextEditor::Range(m_cursor, m_cursor); } } // Move cursor to end (or beginning) of selected word if (m_view->selection()) { #if !defined(Q_OS_OSX) QApplication::clipboard()->setText(m_view->selectionText(), QClipboard::Selection); #endif } moveCursorToSelectionEdge(); m_possibleTripleClick = true; QTimer::singleShot(QApplication::doubleClickInterval(), this, SLOT(tripleClickTimeout())); m_scrollX = 0; m_scrollY = 0; m_scrollTimer.start(50); e->accept(); break; default: e->ignore(); break; } } void KateViewInternal::tripleClickTimeout() { m_possibleTripleClick = false; } void KateViewInternal::mouseReleaseEvent(QMouseEvent *e) { switch (e->button()) { case Qt::LeftButton: m_selectionMode = Default; // m_selectionCached.start().setLine( -1 ); if (m_selChangedByUser) { if (m_view->selection()) { QApplication::clipboard()->setText(m_view->selectionText(), QClipboard::Selection); } moveCursorToSelectionEdge(); m_selChangedByUser = false; } if (m_dragInfo.state == diPending) { placeCursor(e->pos(), e->modifiers() & Qt::ShiftModifier); } else if (m_dragInfo.state == diNone) { m_scrollTimer.stop(); } m_dragInfo.state = diNone; e->accept(); break; case Qt::MidButton: placeCursor(e->pos()); if (doc()->isReadWrite()) { QString clipboard = QApplication::clipboard()->text(QClipboard::Selection); m_view->paste(&clipboard); } e->accept(); break; default: e->ignore(); break; } } void KateViewInternal::leaveEvent(QEvent *) { m_textHintTimer.stop(); // fix bug 194452, scrolling keeps going if you scroll via mouse drag and press and other mouse // button outside the view area if (m_dragInfo.state == diNone) { m_scrollTimer.stop(); } } KTextEditor::Cursor KateViewInternal::coordinatesToCursor(const QPoint &_coord, bool includeBorder) const { QPoint coord(_coord); KTextEditor::Cursor ret = KTextEditor::Cursor::invalid(); if (includeBorder) { coord.rx() -= m_leftBorder->width(); } coord.rx() += startX(); const KateTextLayout &thisLine = yToKateTextLayout(coord.y()); if (thisLine.isValid()) { ret = renderer()->xToCursor(thisLine, coord.x(), !m_view->wrapCursor()); } if (ret.column() > view()->document()->lineLength(ret.line())) { // The cursor is beyond the end of the line; in that case the renderer // gives the index of the character behind the last one. return KTextEditor::Cursor::invalid(); } return ret; } void KateViewInternal::mouseMoveEvent(QMouseEvent *e) { KTextEditor::Cursor newPosition = coordinatesToCursor(e->pos(), false); if (newPosition != m_mouse) { m_mouse = newPosition; mouseMoved(); } if (e->buttons() & Qt::LeftButton) { if (m_dragInfo.state == diPending) { // we had a mouse down, but haven't confirmed a drag yet // if the mouse has moved sufficiently, we will confirm QPoint p(e->pos() - m_dragInfo.start); // we've left the drag square, we can start a real drag operation now if (p.manhattanLength() > QApplication::startDragDistance()) { doDrag(); } return; } else if (m_dragInfo.state == diDragging) { // Don't do anything after a canceled drag until the user lets go of // the mouse button! return; } m_mouseX = e->x(); m_mouseY = e->y(); m_scrollX = 0; m_scrollY = 0; int d = renderer()->lineHeight(); if (m_mouseX < 0) { m_scrollX = -d; } if (m_mouseX > width()) { m_scrollX = d; } if (m_mouseY < 0) { m_mouseY = 0; m_scrollY = -d; } if (m_mouseY > height()) { m_mouseY = height(); m_scrollY = d; } placeCursor(QPoint(m_mouseX, m_mouseY), true); } else { if (isTargetSelected(e->pos())) { // mouse is over selected text. indicate that the text is draggable by setting // the arrow cursor as other Qt text editing widgets do if (m_mouseCursor != Qt::ArrowCursor) { m_mouseCursor = Qt::ArrowCursor; setCursor(m_mouseCursor); } } else { // normal text cursor if (m_mouseCursor != Qt::IBeamCursor) { m_mouseCursor = Qt::IBeamCursor; setCursor(m_mouseCursor); } } //We need to check whether the mouse position is actually within the widget, //because other widgets like the icon border forward their events to this, //and we will create invalid text hint requests if we don't check if (textHintsEnabled() && geometry().contains(parentWidget()->mapFromGlobal(e->globalPos()))) { if (QToolTip::isVisible()) { QToolTip::hideText(); } m_textHintTimer.start(m_textHintDelay); m_textHintPos = e->pos(); } } } void KateViewInternal::updateDirty() { const int h = renderer()->lineHeight(); int currentRectStart = -1; int currentRectEnd = -1; QRegion updateRegion; { for (int i = 0; i < cache()->viewCacheLineCount(); ++i) { if (cache()->viewLine(i).isDirty()) { if (currentRectStart == -1) { currentRectStart = h * i; currentRectEnd = h; } else { currentRectEnd += h; } } else if (currentRectStart != -1) { updateRegion += QRect(0, currentRectStart, width(), currentRectEnd); currentRectStart = -1; currentRectEnd = -1; } } } if (currentRectStart != -1) { updateRegion += QRect(0, currentRectStart, width(), currentRectEnd); } if (!updateRegion.isEmpty()) { if (debugPainting) { qCDebug(LOG_KTE) << "Update dirty region " << updateRegion; } update(updateRegion); } } void KateViewInternal::hideEvent(QHideEvent *e) { Q_UNUSED(e); if (m_view->isCompletionActive()) { m_view->completionWidget()->abortCompletion(); } } void KateViewInternal::paintEvent(QPaintEvent *e) { if (debugPainting) { qCDebug(LOG_KTE) << "GOT PAINT EVENT: Region" << e->region(); } const QRect &unionRect = e->rect(); int xStart = startX() + unionRect.x(); int xEnd = xStart + unionRect.width(); uint h = renderer()->lineHeight(); uint startz = (unionRect.y() / h); uint endz = startz + 1 + (unionRect.height() / h); uint lineRangesSize = cache()->viewCacheLineCount(); const KTextEditor::Cursor pos = m_cursor; QPainter paint(this); paint.setRenderHints(QPainter::Antialiasing); paint.save(); renderer()->setCaretStyle(m_currentInputMode->caretStyle()); renderer()->setShowTabs(doc()->config()->showTabs()); renderer()->setShowTrailingSpaces(doc()->config()->showSpaces()); + renderer()->updateMarkerSize(); int sy = startz * h; paint.translate(unionRect.x(), startz * h); for (uint z = startz; z <= endz; z++) { paint.save(); if ((z >= lineRangesSize) || (cache()->viewLine(z).line() == -1)) { if (!(z >= lineRangesSize)) { cache()->viewLine(z).setDirty(false); } paint.fillRect(0, 0, unionRect.width(), h, renderer()->config()->backgroundColor()); } else { //qCDebug(LOG_KTE)<<"KateViewInternal::paintEvent(QPaintEvent *e):cache()->viewLine"<viewLine(z); /* If viewLine() returns non-zero, then a document line was split in several visual lines, and we're trying to paint visual line that is not the first. In that case, this line was already painted previously, since KateRenderer::paintTextLine paints all visual lines. Except if we're at the start of the region that needs to be painted -- when no previous calls to paintTextLine were made. */ if (!thisLine.viewLine() || z == startz) { //qDebug() << "paint text: line: " << thisLine.line() << " viewLine " << thisLine.viewLine() << " x: " << unionRect.x() << " y: " << unionRect.y() << " width: " << xEnd-xStart << " height: " << h << endl; // first: paint our line paint.translate(QPoint(0, h * - thisLine.viewLine())); paint.setClipRect(QRect(0, 0, unionRect.width(), h * thisLine.kateLineLayout()->viewLineCount())); renderer()->paintTextLine(paint, thisLine.kateLineLayout(), xStart, xEnd, &pos); paint.translate(0, h * thisLine.viewLine()); // second: paint previous line elements, that span into our line like _, bug 335079 if (z > 0) { KateTextLayout &previousLine = cache()->viewLine(z-1); paint.translate(QPoint(0, h * - (previousLine.viewLine() + 1))); renderer()->paintTextLine(paint, previousLine.kateLineLayout(), xStart, xEnd, &pos); paint.translate(0, h * (previousLine.viewLine() + 1)); } /** * line painted, reset and state + mark line as non-dirty */ thisLine.setDirty(false); } } paint.restore(); paint.translate(0, h); sy += h; } paint.restore(); if (m_textAnimation) { m_textAnimation->draw(paint); } } void KateViewInternal::resizeEvent(QResizeEvent *e) { bool expandedHorizontally = width() > e->oldSize().width(); bool expandedVertically = height() > e->oldSize().height(); bool heightChanged = height() != e->oldSize().height(); m_dummy->setFixedSize(m_lineScroll->width(), m_columnScroll->sizeHint().height()); m_madeVisible = false; if (heightChanged) { setAutoCenterLines(m_autoCenterLines, false); m_cachedMaxStartPos.setPosition(-1, -1); } if (m_view->dynWordWrap()) { bool dirtied = false; for (int i = 0; i < cache()->viewCacheLineCount(); i++) { // find the first dirty line // the word wrap updateView algorithm is forced to check all lines after a dirty one KateTextLayout viewLine = cache()->viewLine(i); if (viewLine.wrap() || viewLine.isRightToLeft() || viewLine.width() > width()) { dirtied = true; viewLine.setDirty(); break; } } if (dirtied || heightChanged) { updateView(true); m_leftBorder->update(); } } else { updateView(); if (expandedHorizontally && startX() > 0) { scrollColumns(startX() - (width() - e->oldSize().width())); } } if (width() < e->oldSize().width() && !m_view->wrapCursor()) { // May have to restrain cursor to new smaller width... if (m_cursor.column() > doc()->lineLength(m_cursor.line())) { KateTextLayout thisLine = currentLayout(); KTextEditor::Cursor newCursor(m_cursor.line(), thisLine.endCol() + ((width() - thisLine.xOffset() - (thisLine.width() - m_startX)) / renderer()->spaceWidth()) - 1); if (newCursor.column() < m_cursor.column()) { updateCursor(newCursor); } } } if (expandedVertically) { KTextEditor::Cursor max = maxStartPos(); if (startPos() > max) { scrollPos(max); return; // already fired displayRangeChanged } } emit m_view->displayRangeChanged(m_view); } void KateViewInternal::scrollTimeout() { if (m_scrollX || m_scrollY) { scrollLines(startPos().line() + (m_scrollY / (int) renderer()->lineHeight())); placeCursor(QPoint(m_mouseX, m_mouseY), true); } } void KateViewInternal::cursorTimeout() { if (!debugPainting && m_currentInputMode->blinkCaret()) { renderer()->setDrawCaret(!renderer()->drawCaret()); paintCursor(); } } void KateViewInternal::textHintTimeout() { m_textHintTimer.stop(); KTextEditor::Cursor c = coordinatesToCursor(m_textHintPos, false); if (!c.isValid()) { return; } QStringList textHints; foreach(KTextEditor::TextHintProvider * const p, m_textHintProviders) { const QString hint = p->textHint(m_view, c); if (!hint.isEmpty()) { textHints.append(hint); } } if (!textHints.isEmpty()) { qCDebug(LOG_KTE) << "Hint text: " << textHints; QString hint; foreach(const QString & str, textHints) { hint += QStringLiteral("

%1

").arg(str); } QPoint pos(startX() + m_textHintPos.x(), m_textHintPos.y()); QToolTip::showText(mapToGlobal(pos), hint); } } void KateViewInternal::focusInEvent(QFocusEvent *) { if (QApplication::cursorFlashTime() > 0) { m_cursorTimer.start(QApplication::cursorFlashTime() / 2); } paintCursor(); doc()->setActiveView(m_view); // this will handle focus stuff in kateview m_view->slotGotFocus(); } void KateViewInternal::focusOutEvent(QFocusEvent *) { //if (m_view->isCompletionActive()) //m_view->abortCompletion(); m_cursorTimer.stop(); m_view->renderer()->setDrawCaret(true); paintCursor(); m_textHintTimer.stop(); m_view->slotLostFocus(); } void KateViewInternal::doDrag() { m_dragInfo.state = diDragging; m_dragInfo.dragObject = new QDrag(this); QMimeData *mimeData = new QMimeData(); mimeData->setText(m_view->selectionText()); m_dragInfo.dragObject->setMimeData(mimeData); m_dragInfo.dragObject->start(Qt::MoveAction); } void KateViewInternal::dragEnterEvent(QDragEnterEvent *event) { if (event->source() == this) { event->setDropAction(Qt::MoveAction); } event->setAccepted((event->mimeData()->hasText() && doc()->isReadWrite()) || event->mimeData()->hasUrls()); } void KateViewInternal::fixDropEvent(QDropEvent *event) { if (event->source() != this) { event->setDropAction(Qt::CopyAction); } else { Qt::DropAction action = Qt::MoveAction; #ifdef Q_WS_MAC if (event->keyboardModifiers() & Qt::AltModifier) { action = Qt::CopyAction; } #else if (event->keyboardModifiers() & Qt::ControlModifier) { action = Qt::CopyAction; } #endif event->setDropAction(action); } } void KateViewInternal::dragMoveEvent(QDragMoveEvent *event) { // track the cursor to the current drop location placeCursor(event->pos(), true, false); // important: accept action to switch between copy and move mode // without this, the text will always be copied. fixDropEvent(event); } void KateViewInternal::dropEvent(QDropEvent *event) { /** * if we have urls, pass this event off to the hosting application */ if (event->mimeData()->hasUrls()) { emit dropEventPass(event); return; } if (event->mimeData()->hasText() && doc()->isReadWrite()) { const QString text = event->mimeData()->text(); // is the source our own document? bool priv = false; if (KateViewInternal *vi = qobject_cast(event->source())) { priv = doc()->ownedView(vi->m_view); } // dropped on a text selection area? bool selected = m_view->cursorSelected(m_cursor); fixDropEvent(event); if (priv && selected && event->dropAction() != Qt::CopyAction) { // this is a drag that we started and dropped on our selection // ignore this case return; } // fix the cursor position before editStart(), so that it is correctly // stored for the undo action KTextEditor::Cursor targetCursor(m_cursor); // backup current cursor int selectionWidth = m_view->selectionRange().columnWidth(); // for block selection int selectionHeight = m_view->selectionRange().numberOfLines(); // for block selection if (event->dropAction() != Qt::CopyAction) { editSetCursor(m_view->selectionRange().end()); } else { m_view->clearSelection(); } // use one transaction doc()->editStart(); // on move: remove selected text; on copy: duplicate text doc()->insertText(targetCursor, text, m_view->blockSelection()); KTextEditor::DocumentCursor startCursor(doc(), targetCursor); KTextEditor::DocumentCursor endCursor1(doc(), targetCursor); const int textLength = text.length(); if (event->dropAction() != Qt::CopyAction) { m_view->removeSelectedText(); if (m_cursor.toCursor() < startCursor.toCursor()) { startCursor.move(-textLength); endCursor1.move(-textLength); } } if (!m_view->blockSelection()) { endCursor1.move(textLength); } else { endCursor1.setColumn(startCursor.column() + selectionWidth); endCursor1.setLine(startCursor.line() + selectionHeight); } KTextEditor::Cursor endCursor(endCursor1); qCDebug(LOG_KTE) << startCursor << "---(" << textLength << ")---" << endCursor; setSelection(KTextEditor::Range(startCursor, endCursor)); editSetCursor(endCursor); doc()->editEnd(); event->acceptProposedAction(); updateView(); } // finally finish drag and drop mode m_dragInfo.state = diNone; // important, because the eventFilter`s DragLeave does not occur stopDragScroll(); } //END EVENT HANDLING STUFF void KateViewInternal::clear() { m_startPos.setPosition(0, 0); m_displayCursor = KTextEditor::Cursor(0, 0); m_cursor.setPosition(0, 0); cache()->clear(); updateView(true); } void KateViewInternal::wheelEvent(QWheelEvent *e) { // ctrl pressed -> change font size (only if angle is reported) if (e->modifiers() == Qt::ControlModifier) { if (e->angleDelta().y() > 0) { slotIncFontSizes(qreal(e->angleDelta().y()) / QWheelEvent::DefaultDeltasPerStep); } else if (e->angleDelta().y() < 0) { slotDecFontSizes(qreal(-e->angleDelta().y()) / QWheelEvent::DefaultDeltasPerStep); } // accept always and be done for zooming e->accept(); return; } // handle vertical scrolling via the scrollbar if (e->orientation() == Qt::Vertical) { QWheelEvent copy = *e; QApplication::sendEvent(m_lineScroll, ©); if (copy.isAccepted()) { e->accept(); } } // handle horizontal scrolling via the scrollbar if (e->orientation() == Qt::Horizontal) { // if we have dyn word wrap, we should ignore the scroll events if (m_view->dynWordWrap()) { e->accept(); return; } QWheelEvent copy = *e; QApplication::sendEvent(m_columnScroll, ©); if (copy.isAccepted()) { e->accept(); } } } void KateViewInternal::startDragScroll() { if (!m_dragScrollTimer.isActive()) { m_dragScrollTimer.start(s_scrollTime); } } void KateViewInternal::stopDragScroll() { m_dragScrollTimer.stop(); updateView(); } void KateViewInternal::doDragScroll() { QPoint p = this->mapFromGlobal(QCursor::pos()); int dx = 0, dy = 0; if (p.y() < s_scrollMargin) { dy = p.y() - s_scrollMargin; } else if (p.y() > height() - s_scrollMargin) { dy = s_scrollMargin - (height() - p.y()); } if (p.x() < s_scrollMargin) { dx = p.x() - s_scrollMargin; } else if (p.x() > width() - s_scrollMargin) { dx = s_scrollMargin - (width() - p.x()); } dy /= 4; if (dy) { scrollLines(startPos().line() + dy); } if (columnScrollingPossible() && dx) { scrollColumns(qMin(m_startX + dx, m_columnScroll->maximum())); } if (!dy && !dx) { stopDragScroll(); } } void KateViewInternal::registerTextHintProvider(KTextEditor::TextHintProvider *provider) { if (! m_textHintProviders.contains(provider)) { m_textHintProviders.append(provider); } // we have a client, so start timeout m_textHintTimer.start(m_textHintDelay); } void KateViewInternal::unregisterTextHintProvider(KTextEditor::TextHintProvider *provider) { const int index = m_textHintProviders.indexOf(provider); if (index >= 0) { m_textHintProviders.removeAt(index); } if (m_textHintProviders.isEmpty()) { m_textHintTimer.stop(); } } void KateViewInternal::setTextHintDelay(int delay) { if (delay <= 0) { m_textHintDelay = 200; // ms } else { m_textHintDelay = delay; // ms } } int KateViewInternal::textHintDelay() const { return m_textHintDelay; } bool KateViewInternal::textHintsEnabled() { return ! m_textHintProviders.isEmpty(); } //BEGIN EDIT STUFF void KateViewInternal::editStart() { editSessionNumber++; if (editSessionNumber > 1) { return; } editIsRunning = true; editOldCursor = m_cursor; editOldSelection = m_view->selectionRange(); } void KateViewInternal::editEnd(int editTagLineStart, int editTagLineEnd, bool tagFrom) { if (editSessionNumber == 0) { return; } editSessionNumber--; if (editSessionNumber > 0) { return; } // fix start position, might have moved from column 0 // try to clever calculate the right start column for the tricky dyn word wrap case int col = 0; if (m_view->dynWordWrap()) { if (KateLineLayoutPtr layout = cache()->line(m_startPos.line())) { int index = layout->viewLineForColumn(m_startPos.column()); if (index >= 0 && index < layout->viewLineCount()) { col = layout->viewLine(index).startCol(); } } } m_startPos.setPosition(m_startPos.line(), col); if (tagFrom && (editTagLineStart <= int(m_view->textFolding().visibleLineToLine(startLine())))) { tagAll(); } else { tagLines(editTagLineStart, tagFrom ? qMax(doc()->lastLine() + 1, editTagLineEnd) : editTagLineEnd, true); } if (editOldCursor == m_cursor.toCursor()) { updateBracketMarks(); } updateView(true); if (editOldCursor != m_cursor.toCursor() || m_view == doc()->activeView()) { // Only scroll the view to the cursor if the insertion happens at the cursor. // This might not be the case for e.g. collaborative editing, when a remote user // inserts text at a position not at the caret. if (m_cursor.line() >= editTagLineStart && m_cursor.line() <= editTagLineEnd) { m_madeVisible = false; updateCursor(m_cursor, true); } } /** * selection changed? * fixes bug 316226 */ if (editOldSelection != m_view->selectionRange() || (editOldSelection.isValid() && !editOldSelection.isEmpty() && !(editTagLineStart > editOldSelection.end().line() && editTagLineEnd < editOldSelection.start().line()))) { emit m_view->selectionChanged(m_view); } editIsRunning = false; } void KateViewInternal::editSetCursor(const KTextEditor::Cursor &_cursor) { if (m_cursor.toCursor() != _cursor) { m_cursor.setPosition(_cursor); } } //END void KateViewInternal::viewSelectionChanged() { if (!m_view->selection()) { m_selectAnchor = KTextEditor::Cursor::invalid(); } else { m_selectAnchor = m_view->selectionRange().start(); } // Do NOT nuke the entire range! The reason is that a shift+DC selection // might (correctly) set the range to be empty (i.e. start() == end()), and // subsequent dragging might shrink the selection into non-existence. When // this happens, we use the cached end to restore the cached start so that // updateSelection is not confused. See also comments in updateSelection. m_selectionCached.setStart(KTextEditor::Cursor::invalid()); } KateLayoutCache *KateViewInternal::cache() const { return m_layoutCache; } KTextEditor::Cursor KateViewInternal::toRealCursor(const KTextEditor::Cursor &virtualCursor) const { return KTextEditor::Cursor(m_view->textFolding().visibleLineToLine(virtualCursor.line()), virtualCursor.column()); } KTextEditor::Cursor KateViewInternal::toVirtualCursor(const KTextEditor::Cursor &realCursor) const { /** * only convert valid lines, folding doesn't like invalid input! * don't validate whole cursor, column might be -1 */ if (realCursor.line() < 0) { return KTextEditor::Cursor::invalid(); } return KTextEditor::Cursor(m_view->textFolding().lineToVisibleLine(realCursor.line()), realCursor.column()); } KateRenderer *KateViewInternal::renderer() const { return m_view->renderer(); } void KateViewInternal::mouseMoved() { m_view->notifyMousePositionChanged(m_mouse); m_view->updateRangesIn(KTextEditor::Attribute::ActivateMouseIn); } void KateViewInternal::cursorMoved() { m_view->updateRangesIn(KTextEditor::Attribute::ActivateCaretIn); #ifndef QT_NO_ACCESSIBILITY QAccessibleTextCursorEvent ev(this, KateViewAccessible::positionFromCursor(this, m_cursor)); QAccessible::updateAccessibility(&ev); #endif } bool KateViewInternal::rangeAffectsView(const KTextEditor::Range &range, bool realCursors) const { int startLine = m_startPos.line(); int endLine = startLine + (int)m_visibleLineCount; if (realCursors) { startLine = (int)m_view->textFolding().visibleLineToLine(startLine); endLine = (int)m_view->textFolding().visibleLineToLine(endLine); } return (range.end().line() >= startLine) || (range.start().line() <= endLine); } //BEGIN IM INPUT STUFF QVariant KateViewInternal::inputMethodQuery(Qt::InputMethodQuery query) const { switch (query) { case Qt::ImCursorRectangle: { // Cursor placement code is changed for Asian input method that // shows candidate window. This behavior is same as Qt/E 2.3.7 // which supports Asian input methods. Asian input methods need // start point of IM selection text to place candidate window as // adjacent to the selection text. // // in Qt5, cursor rectangle is used as QRectF internally, and it // will be checked by QRectF::isValid(), which will mark rectangle // with width == 0 or height == 0 as invalid. auto lineHeight = renderer()->lineHeight(); return QRect(cursorToCoordinate(m_cursor, true, false), QSize(1, lineHeight ? lineHeight : 1)); } case Qt::ImFont: return renderer()->currentFont(); case Qt::ImCursorPosition: return m_cursor.column(); case Qt::ImAnchorPosition: // If selectAnchor is at the same line, return the real anchor position // Otherwise return the same position of cursor if (m_view->selection() && m_selectAnchor.line() == m_cursor.line()) { return m_selectAnchor.column(); } else { return m_cursor.column(); } case Qt::ImSurroundingText: if (Kate::TextLine l = doc()->kateTextLine(m_cursor.line())) { return l->string(); } else { return QString(); } case Qt::ImCurrentSelection: if (m_view->selection()) { return m_view->selectionText(); } else { return QString(); } default: /* values: ImMaximumTextLength */ break; } return QWidget::inputMethodQuery(query); } void KateViewInternal::inputMethodEvent(QInputMethodEvent *e) { if (doc()->readOnly()) { e->ignore(); return; } //qCDebug(LOG_KTE) << "Event: cursor" << m_cursor << "commit" << e->commitString() << "preedit" << e->preeditString() << "replacement start" << e->replacementStart() << "length" << e->replacementLength(); if (!m_imPreeditRange) { m_imPreeditRange = doc()->newMovingRange(KTextEditor::Range(m_cursor, m_cursor), KTextEditor::MovingRange::ExpandLeft | KTextEditor::MovingRange::ExpandRight); } if (!m_imPreeditRange->toRange().isEmpty()) { doc()->inputMethodStart(); doc()->removeText(*m_imPreeditRange); doc()->inputMethodEnd(); } if (!e->commitString().isEmpty() || e->replacementLength()) { m_view->removeSelectedText(); KTextEditor::Range preeditRange = *m_imPreeditRange; KTextEditor::Cursor start(m_imPreeditRange->start().line(), m_imPreeditRange->start().column() + e->replacementStart()); KTextEditor::Cursor removeEnd = start + KTextEditor::Cursor(0, e->replacementLength()); doc()->editStart(); if (start != removeEnd) { doc()->removeText(KTextEditor::Range(start, removeEnd)); } if (!e->commitString().isEmpty()) { // if the input method event is text that should be inserted, call KTextEditor::DocumentPrivate::typeChars() // with the text. that method will handle the input and take care of overwrite mode, etc. doc()->typeChars(m_view, e->commitString()); } doc()->editEnd(); // Revert to the same range as above m_imPreeditRange->setRange(preeditRange); } if (!e->preeditString().isEmpty()) { doc()->inputMethodStart(); doc()->insertText(m_imPreeditRange->start(), e->preeditString()); doc()->inputMethodEnd(); // The preedit range gets automatically repositioned } // Finished this input method context? if (m_imPreeditRange && e->preeditString().isEmpty()) { // delete the range and reset the pointer delete m_imPreeditRange; m_imPreeditRange = nullptr; qDeleteAll(m_imPreeditRangeChildren); m_imPreeditRangeChildren.clear(); if (QApplication::cursorFlashTime() > 0) { renderer()->setDrawCaret(false); } renderer()->setCaretOverrideColor(QColor()); e->accept(); return; } KTextEditor::Cursor newCursor = m_cursor; bool hideCursor = false; QColor caretColor; if (m_imPreeditRange) { qDeleteAll(m_imPreeditRangeChildren); m_imPreeditRangeChildren.clear(); int decorationColumn = 0; foreach (const QInputMethodEvent::Attribute &a, e->attributes()) { if (a.type == QInputMethodEvent::Cursor) { newCursor = m_imPreeditRange->start() + KTextEditor::Cursor(0, a.start); hideCursor = !a.length; QColor c = qvariant_cast(a.value); if (c.isValid()) { caretColor = c; } } else if (a.type == QInputMethodEvent::TextFormat) { QTextCharFormat f = qvariant_cast(a.value).toCharFormat(); if (f.isValid() && decorationColumn <= a.start) { KTextEditor::Range fr(m_imPreeditRange->start().line(), m_imPreeditRange->start().column() + a.start, m_imPreeditRange->start().line(), m_imPreeditRange->start().column() + a.start + a.length); KTextEditor::MovingRange *formatRange = doc()->newMovingRange(fr); KTextEditor::Attribute::Ptr attribute(new KTextEditor::Attribute()); attribute->merge(f); formatRange->setAttribute(attribute); decorationColumn = a.start + a.length; m_imPreeditRangeChildren.push_back(formatRange); } } } } renderer()->setDrawCaret(hideCursor); renderer()->setCaretOverrideColor(caretColor); if (newCursor != m_cursor.toCursor()) { updateCursor(newCursor); } e->accept(); } //END IM INPUT STUFF void KateViewInternal::flashChar(const KTextEditor::Cursor &pos, KTextEditor::Attribute::Ptr attribute) { Q_ASSERT(pos.isValid()); Q_ASSERT(attribute.constData()); // if line is folded away, do nothing if (!m_view->textFolding().isLineVisible(pos.line())) { return; } KTextEditor::Range range(pos, KTextEditor::Cursor(pos.line(), pos.column() + 1)); if (m_textAnimation) { m_textAnimation->deleteLater(); } m_textAnimation = new KateTextAnimation(range, attribute, this); } void KateViewInternal::documentTextInserted(KTextEditor::Document *document, const KTextEditor::Range &range) { #ifndef QT_NO_ACCESSIBILITY if (QAccessible::isActive()) { QAccessibleTextInsertEvent ev(this, KateViewAccessible::positionFromCursor(this, range.start()), document->text(range)); QAccessible::updateAccessibility(&ev); } #endif } void KateViewInternal::documentTextRemoved(KTextEditor::Document * /*document*/, const KTextEditor::Range &range, const QString &oldText) { #ifndef QT_NO_ACCESSIBILITY if (QAccessible::isActive()) { QAccessibleTextRemoveEvent ev(this, KateViewAccessible::positionFromCursor(this, range.start()), oldText); QAccessible::updateAccessibility(&ev); } #endif }