diff --git a/src/completion/expandingtree/expandingwidgetmodel.cpp b/src/completion/expandingtree/expandingwidgetmodel.cpp index 8099ecab..a5307167 100644 --- a/src/completion/expandingtree/expandingwidgetmodel.cpp +++ b/src/completion/expandingtree/expandingwidgetmodel.cpp @@ -1,556 +1,556 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2007 David Nolden * * 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. */ #include "expandingwidgetmodel.h" #include #include #include #include #include #include #include #include "kcolorutils.h" #include "expandingdelegate.h" #include "katepartdebug.h" using namespace KTextEditor; inline QModelIndex firstColumn(const QModelIndex &index) { return index.sibling(index.row(), 0); } ExpandingWidgetModel::ExpandingWidgetModel(QWidget *parent) : QAbstractItemModel(parent) { } ExpandingWidgetModel::~ExpandingWidgetModel() { clearExpanding(); } static QColor doAlternate(QColor color) { QColor background = QApplication::palette().background().color(); return KColorUtils::mix(color, background, 0.15); } uint ExpandingWidgetModel::matchColor(const QModelIndex &index) const { int matchQuality = contextMatchQuality(index.sibling(index.row(), 0)); if (matchQuality > 0) { bool alternate = index.row() & 1; QColor badMatchColor(0xff00aa44); //Blue-ish green QColor goodMatchColor(0xff00ff00); //Green QColor background = treeView()->palette().light().color(); QColor totalColor = KColorUtils::mix(badMatchColor, goodMatchColor, ((float)matchQuality) / 10.0); if (alternate) { totalColor = doAlternate(totalColor); } const qreal dynamicTint = 0.2; const qreal minimumTint = 0.2; qreal tintStrength = (dynamicTint * matchQuality) / 10; - if (tintStrength) { + if (tintStrength != 0.0) { tintStrength += minimumTint; //Some minimum tinting strength, else it's not visible any more } return KColorUtils::tint(background, totalColor, tintStrength).rgb(); } else { return 0; } } QVariant ExpandingWidgetModel::data(const QModelIndex &index, int role) const { switch (role) { case Qt::BackgroundRole: { if (index.column() == 0) { //Highlight by match-quality uint color = matchColor(index); if (color) { return QBrush(color); } } //Use a special background-color for expanded items if (isExpanded(index)) { if (index.row() & 1) { return doAlternate(treeView()->palette().toolTipBase().color()); } else { return treeView()->palette().toolTipBase(); } } } } return QVariant(); } void ExpandingWidgetModel::clearMatchQualities() { m_contextMatchQualities.clear(); } QModelIndex ExpandingWidgetModel::partiallyExpandedRow() const { if (m_partiallyExpanded.isEmpty()) { return QModelIndex(); } else { return m_partiallyExpanded.constBegin().key(); } } void ExpandingWidgetModel::clearExpanding() { clearMatchQualities(); QMap oldExpandState = m_expandState; foreach (const QPointer &widget, m_expandingWidgets) if (widget) { widget->deleteLater(); // By using deleteLater, we prevent crashes when an action within a widget makes the completion cancel } m_expandingWidgets.clear(); m_expandState.clear(); m_partiallyExpanded.clear(); for (QMap::const_iterator it = oldExpandState.constBegin(); it != oldExpandState.constEnd(); ++it) if (it.value() == Expanded) { emit dataChanged(it.key(), it.key()); } } ExpandingWidgetModel::ExpansionType ExpandingWidgetModel::isPartiallyExpanded(const QModelIndex &index) const { if (m_partiallyExpanded.contains(firstColumn(index))) { return m_partiallyExpanded[firstColumn(index)]; } else { return NotExpanded; } } void ExpandingWidgetModel::partiallyUnExpand(const QModelIndex &idx_) { QModelIndex index(firstColumn(idx_)); m_partiallyExpanded.remove(index); m_partiallyExpanded.remove(idx_); } int ExpandingWidgetModel::partiallyExpandWidgetHeight() const { return 60; ///@todo use font-metrics text-height*2 for 2 lines } void ExpandingWidgetModel::rowSelected(const QModelIndex &idx_) { QModelIndex idx(firstColumn(idx_)); if (!m_partiallyExpanded.contains(idx)) { QModelIndex oldIndex = partiallyExpandedRow(); //Unexpand the previous partially expanded row if (!m_partiallyExpanded.isEmpty()) { ///@todo allow multiple partially expanded rows while (!m_partiallyExpanded.isEmpty()) { m_partiallyExpanded.erase(m_partiallyExpanded.begin()); } //partiallyUnExpand( m_partiallyExpanded.begin().key() ); } //Notify the underlying models that the item was selected, and eventually get back the text for the expanding widget. if (!idx.isValid()) { //All items have been unselected if (oldIndex.isValid()) { emit dataChanged(oldIndex, oldIndex); } } else { QVariant variant = data(idx, CodeCompletionModel::ItemSelected); if (!isExpanded(idx) && variant.type() == QVariant::String) { //Either expand upwards or downwards, choose in a way that //the visible fields of the new selected entry are not moved. if (oldIndex.isValid() && (oldIndex < idx || (!(oldIndex < idx) && oldIndex.parent() < idx.parent()))) { m_partiallyExpanded.insert(idx, ExpandUpwards); } else { m_partiallyExpanded.insert(idx, ExpandDownwards); } //Say that one row above until one row below has changed, so no items will need to be moved(the space that is taken from one item is given to the other) if (oldIndex.isValid() && oldIndex < idx) { emit dataChanged(oldIndex, idx); if (treeView()->verticalScrollMode() == QAbstractItemView::ScrollPerItem) { //Qt fails to correctly scroll in ScrollPerItem mode, so the selected index is completely visible, //so we do the scrolling by hand. QRect selectedRect = treeView()->visualRect(idx); QRect frameRect = treeView()->frameRect(); if (selectedRect.bottom() > frameRect.bottom()) { int diff = selectedRect.bottom() - frameRect.bottom(); //We need to scroll down QModelIndex newTopIndex = idx; QModelIndex nextTopIndex = idx; QRect nextRect = treeView()->visualRect(nextTopIndex); while (nextTopIndex.isValid() && nextRect.isValid() && nextRect.top() >= diff) { newTopIndex = nextTopIndex; nextTopIndex = treeView()->indexAbove(nextTopIndex); if (nextTopIndex.isValid()) { nextRect = treeView()->visualRect(nextTopIndex); } } treeView()->scrollTo(newTopIndex, QAbstractItemView::PositionAtTop); } } //This is needed to keep the item we are expanding completely visible. Qt does not scroll the view to keep the item visible. //But we must make sure that it isn't too expensive. //We need to make sure that scrolling is efficient, and the whole content is not repainted. //Since we are scrolling anyway, we can keep the next line visible, which might be a cool feature. //Since this also doesn't work smoothly, leave it for now //treeView()->scrollTo( nextLine, QAbstractItemView::EnsureVisible ); } else if (oldIndex.isValid() && idx < oldIndex) { emit dataChanged(idx, oldIndex); //For consistency with the down-scrolling, we keep one additional line visible above the current visible. //Since this also doesn't work smoothly, leave it for now /* QModelIndex prevLine = idx.sibling(idx.row()-1, idx.column()); if( prevLine.isValid() ) treeView()->scrollTo( prevLine );*/ } else { emit dataChanged(idx, idx); } } else if (oldIndex.isValid()) { //We are not partially expanding a new row, but we previously had a partially expanded row. So signalize that it has been unexpanded. emit dataChanged(oldIndex, oldIndex); } } } else { qCDebug(LOG_KTE) << "ExpandingWidgetModel::rowSelected: Row is already partially expanded"; } } QString ExpandingWidgetModel::partialExpandText(const QModelIndex &idx) const { if (!idx.isValid()) { return QString(); } return data(firstColumn(idx), CodeCompletionModel::ItemSelected).toString(); } QRect ExpandingWidgetModel::partialExpandRect(const QModelIndex &idx_) const { QModelIndex idx(firstColumn(idx_)); if (!idx.isValid()) { return QRect(); } ExpansionType expansion = ExpandDownwards; if (m_partiallyExpanded.find(idx) != m_partiallyExpanded.constEnd()) { expansion = m_partiallyExpanded[idx]; } //Get the whole rectangle of the row: QModelIndex rightMostIndex = idx; QModelIndex tempIndex = idx; while ((tempIndex = rightMostIndex.sibling(rightMostIndex.row(), rightMostIndex.column() + 1)).isValid()) { rightMostIndex = tempIndex; } QRect rect = treeView()->visualRect(idx); QRect rightMostRect = treeView()->visualRect(rightMostIndex); rect.setLeft(rect.left() + 20); rect.setRight(rightMostRect.right() - 5); //These offsets must match exactly those used in ExpandingDelegate::sizeHint() int top = rect.top() + 5; int bottom = rightMostRect.bottom() - 5; if (expansion == ExpandDownwards) { top += basicRowHeight(idx); } else { bottom -= basicRowHeight(idx); } rect.setTop(top); rect.setBottom(bottom); return rect; } bool ExpandingWidgetModel::isExpandable(const QModelIndex &idx_) const { QModelIndex idx(firstColumn(idx_)); if (!m_expandState.contains(idx)) { m_expandState.insert(idx, NotExpandable); QVariant v = data(idx, CodeCompletionModel::IsExpandable); if (v.canConvert() && v.toBool()) { m_expandState[idx] = Expandable; } } return m_expandState[idx] != NotExpandable; } bool ExpandingWidgetModel::isExpanded(const QModelIndex &idx_) const { QModelIndex idx(firstColumn(idx_)); return m_expandState.contains(idx) && m_expandState[idx] == Expanded; } void ExpandingWidgetModel::setExpanded(QModelIndex idx_, bool expanded) { QModelIndex idx(firstColumn(idx_)); //qCDebug(LOG_KTE) << "Setting expand-state of row " << idx.row() << " to " << expanded; if (!idx.isValid()) { return; } if (isExpandable(idx)) { if (!expanded && m_expandingWidgets.contains(idx) && m_expandingWidgets[idx]) { m_expandingWidgets[idx]->hide(); } m_expandState[idx] = expanded ? Expanded : Expandable; if (expanded) { partiallyUnExpand(idx); } if (expanded && !m_expandingWidgets.contains(idx)) { QVariant v = data(idx, CodeCompletionModel::ExpandingWidget); if (v.canConvert()) { m_expandingWidgets[idx] = v.value(); } else if (v.canConvert()) { //Create a html widget that shows the given string KTextEdit *edit = new KTextEdit(v.toString()); edit->setReadOnly(true); edit->resize(200, 50); //Make the widget small so it embeds nicely. m_expandingWidgets[idx] = edit; } else { m_expandingWidgets[idx] = nullptr; } } //Eventually partially expand the row if (!expanded && firstColumn(treeView()->currentIndex()) == idx && !isPartiallyExpanded(idx)) { rowSelected(idx); //Partially expand the row. } emit dataChanged(idx, idx); if (treeView()) { treeView()->scrollTo(idx); } } } int ExpandingWidgetModel::basicRowHeight(const QModelIndex &idx_) const { QModelIndex idx(firstColumn(idx_)); ExpandingDelegate *delegate = dynamic_cast(treeView()->itemDelegate(idx)); if (!delegate || !idx.isValid()) { qCDebug(LOG_KTE) << "ExpandingWidgetModel::basicRowHeight: Could not get delegate"; return 15; } return delegate->basicSizeHint(idx).height(); } void ExpandingWidgetModel::placeExpandingWidget(const QModelIndex &idx_) { QModelIndex idx(firstColumn(idx_)); if (!idx.isValid() || !isExpanded(idx)) { return; } QWidget *w = m_expandingWidgets.value(idx); if (!w) { return; } QRect rect = treeView()->visualRect(idx); if (!rect.isValid() || rect.bottom() < 0 || rect.top() >= treeView()->height()) { //The item is currently not visible w->hide(); return; } //Find out the basic width of the row rect.setLeft(rect.left() + 20); for (int i = 0, numColumns = idx.model()->columnCount(idx.parent()); i < numColumns; ++i) { QModelIndex rightMostIndex = idx.sibling(idx.row(), i); int right = treeView()->visualRect(rightMostIndex).right(); if (right > rect.right()) { rect.setRight(right); } } rect.setRight(rect.right() - 5); //These offsets must match exactly those used in KateCompletionDeleage::sizeHint() rect.setTop(rect.top() + basicRowHeight(idx) + 5); rect.setHeight(w->height()); if (w->parent() != treeView()->viewport() || w->geometry() != rect || !w->isVisible()) { w->setParent(treeView()->viewport()); w->setGeometry(rect); w->show(); } } void ExpandingWidgetModel::placeExpandingWidgets() { for (QMap >::const_iterator it = m_expandingWidgets.constBegin(); it != m_expandingWidgets.constEnd(); ++it) { placeExpandingWidget(it.key()); } } int ExpandingWidgetModel::expandingWidgetsHeight() const { int sum = 0; for (QMap >::const_iterator it = m_expandingWidgets.constBegin(); it != m_expandingWidgets.constEnd(); ++it) { if (isExpanded(it.key()) && (*it)) { sum += (*it)->height(); } } return sum; } QWidget *ExpandingWidgetModel::expandingWidget(const QModelIndex &idx_) const { QModelIndex idx(firstColumn(idx_)); if (m_expandingWidgets.contains(idx)) { return m_expandingWidgets[idx]; } else { return nullptr; } } void ExpandingWidgetModel::cacheIcons() const { if (m_expandedIcon.isNull()) { m_expandedIcon = QIcon::fromTheme(QStringLiteral("arrow-down")); } if (m_collapsedIcon.isNull()) { m_collapsedIcon = QIcon::fromTheme(QStringLiteral("arrow-right")); } } QList mergeCustomHighlighting(int leftSize, const QList &left, int rightSize, const QList &right) { QList ret = left; if (left.isEmpty()) { ret << QVariant(0); ret << QVariant(leftSize); ret << QTextFormat(QTextFormat::CharFormat); } if (right.isEmpty()) { ret << QVariant(leftSize); ret << QVariant(rightSize); ret << QTextFormat(QTextFormat::CharFormat); } else { QList::const_iterator it = right.constBegin(); while (it != right.constEnd()) { { QList::const_iterator testIt = it; for (int a = 0; a < 2; a++) { ++testIt; if (testIt == right.constEnd()) { qCWarning(LOG_KTE) << "Length of input is not multiple of 3"; break; } } } ret << QVariant((*it).toInt() + leftSize); ++it; ret << QVariant((*it).toInt()); ++it; ret << *it; if (!(*it).value().isValid()) { qCDebug(LOG_KTE) << "Text-format is invalid"; } ++it; } } return ret; } //It is assumed that between each two strings, one space is inserted QList mergeCustomHighlighting(QStringList strings, QList highlights, int grapBetweenStrings) { if (strings.isEmpty()) { qCWarning(LOG_KTE) << "List of strings is empty"; return QList(); } if (highlights.isEmpty()) { qCWarning(LOG_KTE) << "List of highlightings is empty"; return QList(); } if (strings.count() != highlights.count()) { qCWarning(LOG_KTE) << "Length of string-list is " << strings.count() << " while count of highlightings is " << highlights.count() << ", should be same"; return QList(); } //Merge them together QString totalString = strings[0]; QVariantList totalHighlighting = highlights[0]; strings.pop_front(); highlights.pop_front(); while (!strings.isEmpty()) { totalHighlighting = mergeCustomHighlighting(totalString.length(), totalHighlighting, strings[0].length(), highlights[0]); totalString += strings[0]; for (int a = 0; a < grapBetweenStrings; a++) { totalString += QLatin1Char(' '); } strings.pop_front(); highlights.pop_front(); } //Combine the custom-highlightings return totalHighlighting; } diff --git a/src/completion/katecompletionconfig.h b/src/completion/katecompletionconfig.h index 3999ba46..03540f29 100644 --- a/src/completion/katecompletionconfig.h +++ b/src/completion/katecompletionconfig.h @@ -1,83 +1,83 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2006 Hamish Rodda * * 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 KATECOMPLETIONCONFIG_H #define KATECOMPLETIONCONFIG_H #include #include "kateconfig.h" namespace Ui { class CompletionConfigWidget; } class QTreeWidgetItem; class KateCompletionModel; /** * @author Hamish Rodda */ class KateCompletionConfig : public QDialog, public KateConfig { Q_OBJECT public: explicit KateCompletionConfig(KateCompletionModel *model, QWidget *parent = nullptr); - virtual ~KateCompletionConfig(); + ~KateCompletionConfig() Q_DECL_OVERRIDE; /** * Read config from object */ void readConfig(const KConfigGroup &config); /** * Write config to object */ void writeConfig(KConfigGroup &config); public Q_SLOTS: void apply(); protected: void updateConfig() Q_DECL_OVERRIDE; private Q_SLOTS: void moveColumnUp(); void moveColumnDown(); void moveGroupingUp(); void moveGroupingDown(); void moveGroupingOrderUp(); void moveGroupingOrderDown(); private: void applyInternal(); Ui::CompletionConfigWidget *ui; KateCompletionModel *m_model; QTreeWidgetItem *m_groupingScopeType; QTreeWidgetItem *m_groupingScope; QTreeWidgetItem *m_groupingAccessType; QTreeWidgetItem *m_groupingItemType; }; #endif diff --git a/src/completion/katecompletionmodel.cpp b/src/completion/katecompletionmodel.cpp index 3a1ef585..66520f00 100644 --- a/src/completion/katecompletionmodel.cpp +++ b/src/completion/katecompletionmodel.cpp @@ -1,2404 +1,2404 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2005-2006 Hamish Rodda * Copyright (C) 2007-2008 David Nolden * * 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. */ #include "katecompletionmodel.h" #include "katecompletionwidget.h" #include "katecompletiontree.h" #include "katecompletiondelegate.h" #include "kateargumenthintmodel.h" #include "kateview.h" #include "katerenderer.h" #include "kateconfig.h" #include #include "katepartdebug.h" #include #include #include #include #include #include using namespace KTextEditor; ///A helper-class for handling completion-models with hierarchical grouping/optimization class HierarchicalModelHandler { public: explicit HierarchicalModelHandler(CodeCompletionModel *model); void addValue(CodeCompletionModel::ExtraItemDataRoles role, const QVariant &value); //Walks the index upwards and collects all defined completion-roles on the way void collectRoles(const QModelIndex &index); void takeRole(const QModelIndex &index); CodeCompletionModel *model() const; //Assumes that index is a sub-index of the indices where role-values were taken QVariant getData(CodeCompletionModel::ExtraItemDataRoles role, const QModelIndex &index) const; bool hasHierarchicalRoles() const; int inheritanceDepth(const QModelIndex &i) const; QString customGroup() const { return m_customGroup; } int customGroupingKey() const { return m_groupSortingKey; } private: typedef QMap RoleMap; RoleMap m_roleValues; QString m_customGroup; int m_groupSortingKey; CodeCompletionModel *m_model; }; CodeCompletionModel *HierarchicalModelHandler::model() const { return m_model; } bool HierarchicalModelHandler::hasHierarchicalRoles() const { return !m_roleValues.isEmpty(); } void HierarchicalModelHandler::collectRoles(const QModelIndex &index) { if (index.parent().isValid()) { collectRoles(index.parent()); } if (m_model->rowCount(index) != 0) { takeRole(index); } } int HierarchicalModelHandler::inheritanceDepth(const QModelIndex &i) const { return getData(CodeCompletionModel::InheritanceDepth, i).toInt(); } void HierarchicalModelHandler::takeRole(const QModelIndex &index) { QVariant v = index.data(CodeCompletionModel::GroupRole); if (v.isValid() && v.canConvert(QVariant::Int)) { QVariant value = index.data(v.toInt()); if (v.toInt() == Qt::DisplayRole) { m_customGroup = index.data(Qt::DisplayRole).toString(); QVariant sortingKey = index.data(CodeCompletionModel::InheritanceDepth); if (sortingKey.canConvert(QVariant::Int)) { m_groupSortingKey = sortingKey.toInt(); } } else { m_roleValues[(CodeCompletionModel::ExtraItemDataRoles)v.toInt()] = value; } } else { qCDebug(LOG_KTE) << "Did not return valid GroupRole in hierarchical completion-model"; } } QVariant HierarchicalModelHandler::getData(CodeCompletionModel::ExtraItemDataRoles role, const QModelIndex &index) const { RoleMap::const_iterator it = m_roleValues.find(role); if (it != m_roleValues.end()) { return *it; } else { return index.data(role); } } HierarchicalModelHandler::HierarchicalModelHandler(CodeCompletionModel *model) : m_groupSortingKey(-1), m_model(model) { } void HierarchicalModelHandler::addValue(CodeCompletionModel::ExtraItemDataRoles role, const QVariant &value) { m_roleValues[role] = value; } KateCompletionModel::KateCompletionModel(KateCompletionWidget *parent) : ExpandingWidgetModel(parent) , m_ungrouped(new Group({}, 0, this)) , m_argumentHints(new Group(i18n("Argument-hints"), -1, this)) , m_bestMatches(new Group(i18n("Best matches"), BestMatchesProperty, this)) , m_filterAttributes(KTextEditor::CodeCompletionModel::NoProperty) { m_emptyGroups.append(m_ungrouped); m_emptyGroups.append(m_argumentHints); m_emptyGroups.append(m_bestMatches); m_updateBestMatchesTimer = new QTimer(this); m_updateBestMatchesTimer->setSingleShot(true); connect(m_updateBestMatchesTimer, SIGNAL(timeout()), this, SLOT(updateBestMatches())); m_groupHash.insert(0, m_ungrouped); m_groupHash.insert(-1, m_argumentHints); m_groupHash.insert(BestMatchesProperty, m_argumentHints); } KateCompletionModel::~KateCompletionModel() { clearCompletionModels(); delete m_argumentHints; delete m_ungrouped; delete m_bestMatches; } QTreeView *KateCompletionModel::treeView() const { return view()->completionWidget()->treeView(); } QVariant KateCompletionModel::data(const QModelIndex &index, int role) const { if (!hasCompletionModel() || !index.isValid()) { return QVariant(); } if (role == Qt::DecorationRole && index.column() == KTextEditor::CodeCompletionModel::Prefix && isExpandable(index)) { cacheIcons(); if (!isExpanded(index)) { return QVariant(m_collapsedIcon); } else { return QVariant(m_expandedIcon); } } //groupOfParent returns a group when the index is a member of that group, but not the group head/label. if (!hasGroups() || groupOfParent(index)) { if ( role == Qt::TextAlignmentRole ) { if (isColumnMergingEnabled() && !m_columnMerges.isEmpty()) { int c = 0; foreach (const QList &list, m_columnMerges) { if (index.column() < c + list.size()) { c += list.size(); continue; } else if (list.count() == 1 && list.first() == CodeCompletionModel::Scope) { return Qt::AlignRight; } else { return QVariant(); } } } else if ((!isColumnMergingEnabled() || m_columnMerges.isEmpty()) && index.column() == CodeCompletionModel::Scope) { return Qt::AlignRight; } } // Merge text for column merging if (role == Qt::DisplayRole && !m_columnMerges.isEmpty() && isColumnMergingEnabled()) { QString text; foreach (int column, m_columnMerges[index.column()]) { QModelIndex sourceIndex = mapToSource(createIndex(index.row(), column, index.internalPointer())); text.append(sourceIndex.data(role).toString()); } return text; } if (role == CodeCompletionModel::HighlightingMethod) { //Return that we are doing custom-highlighting of one of the sub-strings does it. Unfortunately internal highlighting does not work for the other substrings. foreach (int column, m_columnMerges[index.column()]) { QModelIndex sourceIndex = mapToSource(createIndex(index.row(), column, index.internalPointer())); QVariant method = sourceIndex.data(CodeCompletionModel::HighlightingMethod); if (method.type() == QVariant::Int && method.toInt() == CodeCompletionModel::CustomHighlighting) { return QVariant(CodeCompletionModel::CustomHighlighting); } } return QVariant(); } if (role == CodeCompletionModel::CustomHighlight) { //Merge custom highlighting if multiple columns were merged QStringList strings; //Collect strings foreach (int column, m_columnMerges[index.column()]) { strings << mapToSource(createIndex(index.row(), column, index.internalPointer())).data(Qt::DisplayRole).toString(); } QList highlights; //Collect custom-highlightings foreach (int column, m_columnMerges[index.column()]) { highlights << mapToSource(createIndex(index.row(), column, index.internalPointer())).data(CodeCompletionModel::CustomHighlight).toList(); } return mergeCustomHighlighting(strings, highlights, 0); } QVariant v = mapToSource(index).data(role); if (v.isValid()) { return v; } else { return ExpandingWidgetModel::data(index, role); } } //Returns a nonzero group if this index is the head of a group(A Label in the list) Group *g = groupForIndex(index); if (g && (!g->isEmpty)) { switch (role) { case Qt::DisplayRole: if (!index.column()) { return g->title; } break; case Qt::FontRole: if (!index.column()) { QFont f = view()->renderer()->config()->font(); f.setBold(true); return f; } break; case Qt::ForegroundRole: return QApplication::palette().toolTipText().color(); case Qt::BackgroundRole: return QApplication::palette().toolTipBase().color(); } } return QVariant(); } int KateCompletionModel::contextMatchQuality(const QModelIndex &index) const { if (!index.isValid()) { return 0; } Group *g = groupOfParent(index); if (!g || g->filtered.size() < index.row()) { return 0; } return contextMatchQuality(g->filtered[index.row()].sourceRow()); } int KateCompletionModel::contextMatchQuality(const ModelRow &source) const { QModelIndex realIndex = source.second; int bestMatch = -1; //Iterate through all argument-hints and find the best match-quality foreach (const Item &item, m_argumentHints->filtered) { const ModelRow &row(item.sourceRow()); if (realIndex.model() != row.first) { continue; //We can only match within the same source-model } QModelIndex hintIndex = row.second; QVariant depth = hintIndex.data(CodeCompletionModel::ArgumentHintDepth); if (!depth.isValid() || depth.type() != QVariant::Int || depth.toInt() != 1) { continue; //Only match completion-items to argument-hints of depth 1(the ones the item will be given to as argument) } hintIndex.data(CodeCompletionModel::SetMatchContext); QVariant matchQuality = realIndex.data(CodeCompletionModel::MatchQuality); if (matchQuality.isValid() && matchQuality.type() == QVariant::Int) { int m = matchQuality.toInt(); if (m > bestMatch) { bestMatch = m; } } } if (m_argumentHints->filtered.isEmpty()) { QVariant matchQuality = realIndex.data(CodeCompletionModel::MatchQuality); if (matchQuality.isValid() && matchQuality.type() == QVariant::Int) { int m = matchQuality.toInt(); if (m > bestMatch) { bestMatch = m; } } } return bestMatch; } Qt::ItemFlags KateCompletionModel::flags(const QModelIndex &index) const { if (!hasCompletionModel() || !index.isValid()) { return Qt::NoItemFlags; } if (!hasGroups() || groupOfParent(index)) { return Qt::ItemIsSelectable | Qt::ItemIsEnabled; } return Qt::ItemIsEnabled; } KateCompletionWidget *KateCompletionModel::widget() const { return static_cast(QObject::parent()); } KTextEditor::ViewPrivate *KateCompletionModel::view() const { return widget()->view(); } void KateCompletionModel::setMatchCaseSensitivity(Qt::CaseSensitivity cs) { m_matchCaseSensitivity = cs; } int KateCompletionModel::columnCount(const QModelIndex &) const { return isColumnMergingEnabled() && !m_columnMerges.isEmpty() ? m_columnMerges.count() : KTextEditor::CodeCompletionModel::ColumnCount; } KateCompletionModel::ModelRow KateCompletionModel::modelRowPair(const QModelIndex &index) const { return qMakePair(static_cast(const_cast(index.model())), index); } bool KateCompletionModel::hasChildren(const QModelIndex &parent) const { if (!hasCompletionModel()) { return false; } if (!parent.isValid()) { if (hasGroups()) { return true; } return !m_ungrouped->filtered.isEmpty(); } if (parent.column() != 0) { return false; } if (!hasGroups()) { return false; } if (Group *g = groupForIndex(parent)) { return !g->filtered.isEmpty(); } return false; } QModelIndex KateCompletionModel::index(int row, int column, const QModelIndex &parent) const { if (row < 0 || column < 0 || column >= columnCount(QModelIndex())) { return QModelIndex(); } if (parent.isValid() || !hasGroups()) { if (parent.isValid() && parent.column() != 0) { return QModelIndex(); } Group *g = groupForIndex(parent); if (!g) { return QModelIndex(); } if (row >= g->filtered.count()) { //qCWarning(LOG_KTE) << "Invalid index requested: row " << row << " beyond indivdual range in group " << g; return QModelIndex(); } //qCDebug(LOG_KTE) << "Returning index for child " << row << " of group " << g; return createIndex(row, column, g); } if (row >= m_rowTable.count()) { //qCWarning(LOG_KTE) << "Invalid index requested: row " << row << " beyond group range."; return QModelIndex(); } //qCDebug(LOG_KTE) << "Returning index for group " << m_rowTable[row]; return createIndex(row, column, quintptr(0)); } /*QModelIndex KateCompletionModel::sibling( int row, int column, const QModelIndex & index ) const { if (row < 0 || column < 0 || column >= columnCount(QModelIndex())) return QModelIndex(); if (!index.isValid()) { } if (Group* g = groupOfParent(index)) { if (row >= g->filtered.count()) return QModelIndex(); return createIndex(row, column, g); } if (hasGroups()) return QModelIndex(); if (row >= m_ungrouped->filtered.count()) return QModelIndex(); return createIndex(row, column, m_ungrouped); }*/ bool KateCompletionModel::hasIndex(int row, int column, const QModelIndex &parent) const { if (row < 0 || column < 0 || column >= columnCount(QModelIndex())) { return false; } if (parent.isValid() || !hasGroups()) { if (parent.isValid() && parent.column() != 0) { return false; } Group *g = groupForIndex(parent); if (row >= g->filtered.count()) { return false; } return true; } if (row >= m_rowTable.count()) { return false; } return true; } QModelIndex KateCompletionModel::indexForRow(Group *g, int row) const { if (row < 0 || row >= g->filtered.count()) { return QModelIndex(); } return createIndex(row, 0, g); } QModelIndex KateCompletionModel::indexForGroup(Group *g) const { if (!hasGroups()) { return QModelIndex(); } int row = m_rowTable.indexOf(g); if (row == -1) { return QModelIndex(); } return createIndex(row, 0, quintptr(0)); } void KateCompletionModel::clearGroups() { clearExpanding(); m_ungrouped->clear(); m_argumentHints->clear(); m_bestMatches->clear(); // Don't bother trying to work out where it is m_rowTable.removeAll(m_ungrouped); m_emptyGroups.removeAll(m_ungrouped); m_rowTable.removeAll(m_argumentHints); m_emptyGroups.removeAll(m_argumentHints); m_rowTable.removeAll(m_bestMatches); m_emptyGroups.removeAll(m_bestMatches); qDeleteAll(m_rowTable); qDeleteAll(m_emptyGroups); m_rowTable.clear(); m_emptyGroups.clear(); m_groupHash.clear(); m_customGroupHash.clear(); m_emptyGroups.append(m_ungrouped); m_groupHash.insert(0, m_ungrouped); m_emptyGroups.append(m_argumentHints); m_groupHash.insert(-1, m_argumentHints); m_emptyGroups.append(m_bestMatches); m_groupHash.insert(BestMatchesProperty, m_bestMatches); } QSet KateCompletionModel::createItems(const HierarchicalModelHandler &_handler, const QModelIndex &i, bool notifyModel) { HierarchicalModelHandler handler(_handler); QSet ret; if (handler.model()->rowCount(i) == 0) { //Leaf node, create an item ret.insert(createItem(handler, i, notifyModel)); } else { //Non-leaf node, take the role from the node, and recurse to the sub-nodes handler.takeRole(i); for (int a = 0; a < handler.model()->rowCount(i); a++) { ret += createItems(handler, i.child(a, 0), notifyModel); } } return ret; } QSet KateCompletionModel::deleteItems(const QModelIndex &i) { QSet ret; if (i.model()->rowCount(i) == 0) { //Leaf node, delete the item Group *g = groupForIndex(mapFromSource(i)); ret.insert(g); g->removeItem(ModelRow(const_cast(static_cast(i.model())), i)); } else { //Non-leaf node for (int a = 0; a < i.model()->rowCount(i); a++) { ret += deleteItems(i.child(a, 0)); } } return ret; } void KateCompletionModel::createGroups() { beginResetModel(); //After clearing the model, it has to be reset, else we will be in an invalid state while inserting //new groups. clearGroups(); bool has_groups = false; foreach (CodeCompletionModel *sourceModel, m_completionModels) { has_groups |= sourceModel->hasGroups(); for (int i = 0; i < sourceModel->rowCount(); ++i) { createItems(HierarchicalModelHandler(sourceModel), sourceModel->index(i, 0)); } } m_hasGroups = has_groups; //debugStats(); foreach (Group *g, m_rowTable) { hideOrShowGroup(g); } foreach (Group *g, m_emptyGroups) { hideOrShowGroup(g); } makeGroupItemsUnique(); updateBestMatches(); endResetModel(); } KateCompletionModel::Group *KateCompletionModel::createItem(const HierarchicalModelHandler &handler, const QModelIndex &sourceIndex, bool notifyModel) { //QModelIndex sourceIndex = sourceModel->index(row, CodeCompletionModel::Name, QModelIndex()); int completionFlags = handler.getData(CodeCompletionModel::CompletionRole, sourceIndex).toInt(); //Scope is expensive, should not be used with big models QString scopeIfNeeded = (groupingMethod() & Scope) ? sourceIndex.sibling(sourceIndex.row(), CodeCompletionModel::Scope).data(Qt::DisplayRole).toString() : QString(); int argumentHintDepth = handler.getData(CodeCompletionModel::ArgumentHintDepth, sourceIndex).toInt(); Group *g; if (argumentHintDepth) { g = m_argumentHints; } else { QString customGroup = handler.customGroup(); if (!customGroup.isNull() && m_hasGroups) { if (m_customGroupHash.contains(customGroup)) { g = m_customGroupHash[customGroup]; } else { g = new Group(customGroup, 0, this); g->customSortingKey = handler.customGroupingKey(); m_emptyGroups.append(g); m_customGroupHash.insert(customGroup, g); } } else { g = fetchGroup(completionFlags, scopeIfNeeded, handler.hasHierarchicalRoles()); } } Item item = Item(g != m_argumentHints, this, handler, ModelRow(handler.model(), sourceIndex)); if (g != m_argumentHints) { item.match(); } g->addItem(item, notifyModel); return g; } void KateCompletionModel::slotRowsInserted(const QModelIndex &parent, int start, int end) { QSet affectedGroups; HierarchicalModelHandler handler(static_cast(sender())); if (parent.isValid()) { handler.collectRoles(parent); } for (int i = start; i <= end; ++i) { affectedGroups += createItems(handler, parent.isValid() ? parent.child(i, 0) : handler.model()->index(i, 0), true); } foreach (Group *g, affectedGroups) { hideOrShowGroup(g, true); } } void KateCompletionModel::slotRowsRemoved(const QModelIndex &parent, int start, int end) { CodeCompletionModel *source = static_cast(sender()); QSet affectedGroups; for (int i = start; i <= end; ++i) { QModelIndex index = parent.isValid() ? parent.child(i, 0) : source->index(i, 0); affectedGroups += deleteItems(index); } foreach (Group *g, affectedGroups) { hideOrShowGroup(g, true); } } KateCompletionModel::Group *KateCompletionModel::fetchGroup(int attribute, const QString &scope, bool forceGrouping) { Q_UNUSED(forceGrouping); ///@todo use forceGrouping if (!hasGroups()) { return m_ungrouped; } int groupingAttribute = groupingAttributes(attribute); //qCDebug(LOG_KTE) << attribute << " " << groupingAttribute; if (m_groupHash.contains(groupingAttribute)) { if (groupingMethod() & Scope) { for (QHash::ConstIterator it = m_groupHash.constFind(groupingAttribute); it != m_groupHash.constEnd() && it.key() == groupingAttribute; ++it) if (it.value()->scope == scope) { return it.value(); } } else { return m_groupHash.value(groupingAttribute); } } QString st, at, it; QString title; if (groupingMethod() & ScopeType) { if (attribute & KTextEditor::CodeCompletionModel::GlobalScope) { st = QStringLiteral("Global"); } else if (attribute & KTextEditor::CodeCompletionModel::NamespaceScope) { st = QStringLiteral("Namespace"); } else if (attribute & KTextEditor::CodeCompletionModel::LocalScope) { st = QStringLiteral("Local"); } title = st; } if (groupingMethod() & Scope) { if (!title.isEmpty()) { title.append(QLatin1String(" ")); } title.append(scope); } if (groupingMethod() & AccessType) { if (attribute & KTextEditor::CodeCompletionModel::Public) { at = QStringLiteral("Public"); } else if (attribute & KTextEditor::CodeCompletionModel::Protected) { at = QStringLiteral("Protected"); } else if (attribute & KTextEditor::CodeCompletionModel::Private) { at = QStringLiteral("Private"); } if (accessIncludeStatic() && attribute & KTextEditor::CodeCompletionModel::Static) { at.append(QLatin1String(" Static")); } if (accessIncludeConst() && attribute & KTextEditor::CodeCompletionModel::Const) { at.append(QLatin1String(" Const")); } if (!at.isEmpty()) { if (!title.isEmpty()) { title.append(QLatin1String(", ")); } title.append(at); } } if (groupingMethod() & ItemType) { if (attribute & CodeCompletionModel::Namespace) { it = i18n("Namespaces"); } else if (attribute & CodeCompletionModel::Class) { it = i18n("Classes"); } else if (attribute & CodeCompletionModel::Struct) { it = i18n("Structs"); } else if (attribute & CodeCompletionModel::Union) { it = i18n("Unions"); } else if (attribute & CodeCompletionModel::Function) { it = i18n("Functions"); } else if (attribute & CodeCompletionModel::Variable) { it = i18n("Variables"); } else if (attribute & CodeCompletionModel::Enum) { it = i18n("Enumerations"); } if (!it.isEmpty()) { if (!title.isEmpty()) { title.append(QLatin1String(" ")); } title.append(it); } } Group *ret = new Group(title, attribute, this); ret->scope = scope; m_emptyGroups.append(ret); m_groupHash.insert(groupingAttribute, ret); return ret; } bool KateCompletionModel::hasGroups() const { //qCDebug(LOG_KTE) << "m_groupHash.size()"<= m_rowTable.count()) { return m_ungrouped; } return m_rowTable[index.row()]; } /*QMap< int, QVariant > KateCompletionModel::itemData( const QModelIndex & index ) const { if (!hasGroups() || groupOfParent(index)) { QModelIndex index = mapToSource(index); if (index.isValid()) return index.model()->itemData(index); } return QAbstractItemModel::itemData(index); }*/ QModelIndex KateCompletionModel::parent(const QModelIndex &index) const { if (!index.isValid()) { return QModelIndex(); } if (Group *g = groupOfParent(index)) { if (!hasGroups()) { Q_ASSERT(g == m_ungrouped); return QModelIndex(); } int row = m_rowTable.indexOf(g); if (row == -1) { qCWarning(LOG_KTE) << "Couldn't find parent for index" << index; return QModelIndex(); } return createIndex(row, 0, quintptr(0)); } return QModelIndex(); } int KateCompletionModel::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) { if (hasGroups()) { //qCDebug(LOG_KTE) << "Returning row count for toplevel " << m_rowTable.count(); return m_rowTable.count(); } else { //qCDebug(LOG_KTE) << "Returning ungrouped row count for toplevel " << m_ungrouped->filtered.count(); return m_ungrouped->filtered.count(); } } if (parent.column() > 0) { // only the first column has children return 0; } Group *g = groupForIndex(parent); // This is not an error, seems you don't have to check hasChildren() if (!g) { return 0; } //qCDebug(LOG_KTE) << "Returning row count for group " << g << " as " << g->filtered.count(); return g->filtered.count(); } void KateCompletionModel::sort(int column, Qt::SortOrder order) { Q_UNUSED(column) Q_UNUSED(order) } QModelIndex KateCompletionModel::mapToSource(const QModelIndex &proxyIndex) const { if (!proxyIndex.isValid()) { return QModelIndex(); } if (Group *g = groupOfParent(proxyIndex)) { if (proxyIndex.row() >= 0 && proxyIndex.row() < g->filtered.count()) { ModelRow source = g->filtered[proxyIndex.row()].sourceRow(); return source.second.sibling(source.second.row(), proxyIndex.column()); } else { qCDebug(LOG_KTE) << "Invalid proxy-index"; } } return QModelIndex(); } QModelIndex KateCompletionModel::mapFromSource(const QModelIndex &sourceIndex) const { if (!sourceIndex.isValid()) { return QModelIndex(); } if (!hasGroups()) { return index(m_ungrouped->rowOf(modelRowPair(sourceIndex)), sourceIndex.column(), QModelIndex()); } foreach (Group *g, m_rowTable) { int row = g->rowOf(modelRowPair(sourceIndex)); if (row != -1) { return index(row, sourceIndex.column(), indexForGroup(g)); } } // Copied from above foreach (Group *g, m_emptyGroups) { int row = g->rowOf(modelRowPair(sourceIndex)); if (row != -1) { return index(row, sourceIndex.column(), indexForGroup(g)); } } return QModelIndex(); } void KateCompletionModel::setCurrentCompletion(KTextEditor::CodeCompletionModel *model, const QString &completion) { if (m_currentMatch[model] == completion) { return; } if (!hasCompletionModel()) { m_currentMatch[model] = completion; return; } changeTypes changeType = Change; if (m_currentMatch[model].length() > completion.length() && m_currentMatch[model].startsWith(completion, m_matchCaseSensitivity)) { // Filter has been broadened changeType = Broaden; } else if (m_currentMatch[model].length() < completion.length() && completion.startsWith(m_currentMatch[model], m_matchCaseSensitivity)) { // Filter has been narrowed changeType = Narrow; } //qCDebug(LOG_KTE) << model << "Old match: " << m_currentMatch[model] << ", new: " << completion << ", type: " << changeType; m_currentMatch[model] = completion; const bool resetModel = (changeType != Narrow); if (resetModel) { beginResetModel(); } if (!hasGroups()) { changeCompletions(m_ungrouped, changeType, !resetModel); } else { foreach (Group *g, m_rowTable) { if (g != m_argumentHints) { changeCompletions(g, changeType, !resetModel); } } foreach (Group *g, m_emptyGroups) { if (g != m_argumentHints) { changeCompletions(g, changeType, !resetModel); } } } // NOTE: best matches are also updated in resort resort(); if (resetModel) { endResetModel(); } clearExpanding(); //We need to do this, or be aware of expanding-widgets while filtering. emit layoutChanged(); } QString KateCompletionModel::commonPrefixInternal(const QString &forcePrefix) const { QString commonPrefix; // isNull() = true QList< Group * > groups = m_rowTable; groups += m_ungrouped; foreach (Group *g, groups) { foreach (const Item &item, g->filtered) { uint startPos = m_currentMatch[item.sourceRow().first].length(); const QString candidate = item.name().mid(startPos); if (!candidate.startsWith(forcePrefix)) { continue; } if (commonPrefix.isNull()) { commonPrefix = candidate; //Replace QString::null prefix with QString(), so we won't initialize it again if (commonPrefix.isNull()) { commonPrefix = QString(); // isEmpty() = true, isNull() = false } } else { commonPrefix = commonPrefix.left(candidate.length()); for (int a = 0; a < commonPrefix.length(); ++a) { if (commonPrefix[a] != candidate[a]) { commonPrefix = commonPrefix.left(a); break; } } } } } return commonPrefix; } QString KateCompletionModel::commonPrefix(QModelIndex selectedIndex) const { QString commonPrefix = commonPrefixInternal(QString()); if (commonPrefix.isEmpty() && selectedIndex.isValid()) { Group *g = m_ungrouped; if (hasGroups()) { g = groupOfParent(selectedIndex); } if (g && selectedIndex.row() < g->filtered.size()) { //Follow the path of the selected item, finding the next non-empty common prefix Item item = g->filtered[selectedIndex.row()]; int matchLength = m_currentMatch[item.sourceRow().first].length(); commonPrefix = commonPrefixInternal(item.name().mid(matchLength).left(1)); } } return commonPrefix; } void KateCompletionModel::changeCompletions(Group *g, changeTypes changeType, bool notifyModel) { if (changeType != Narrow) { g->filtered = g->prefilter; //In the "Broaden" or "Change" case, just re-filter everything, //and don't notify the model. The model is notified afterwards through a reset(). } //This code determines what of the filtered items still fit, and computes the ranges that were removed, giving //them to beginRemoveRows(..) in batches QList newFiltered; int deleteUntil = -1; //In each state, the range [currentRow+1, deleteUntil] needs to be deleted for (int currentRow = g->filtered.count() - 1; currentRow >= 0; --currentRow) { if (g->filtered[currentRow].match()) { //This row does not need to be deleted, which means that currentRow+1 to deleteUntil need to be deleted now if (deleteUntil != -1 && notifyModel) { beginRemoveRows(indexForGroup(g), currentRow + 1, deleteUntil); endRemoveRows(); } deleteUntil = -1; newFiltered.prepend(g->filtered[currentRow]); } else { if (deleteUntil == -1) { deleteUntil = currentRow; //Mark that this row needs to be deleted } } } if (deleteUntil != -1 && notifyModel) { beginRemoveRows(indexForGroup(g), 0, deleteUntil); endRemoveRows(); } g->filtered = newFiltered; hideOrShowGroup(g, notifyModel); } int KateCompletionModel::Group::orderNumber() const { if (this == model->m_ungrouped) { return 700; } if (customSortingKey != -1) { return customSortingKey; } if (attribute & BestMatchesProperty) { return 1; } if (attribute & KTextEditor::CodeCompletionModel::LocalScope) { return 100; } else if (attribute & KTextEditor::CodeCompletionModel::Public) { return 200; } else if (attribute & KTextEditor::CodeCompletionModel::Protected) { return 300; } else if (attribute & KTextEditor::CodeCompletionModel::Private) { return 400; } else if (attribute & KTextEditor::CodeCompletionModel::NamespaceScope) { return 500; } else if (attribute & KTextEditor::CodeCompletionModel::GlobalScope) { return 600; } return 700; } bool KateCompletionModel::Group::orderBefore(Group *other) const { return orderNumber() < other->orderNumber(); } void KateCompletionModel::hideOrShowGroup(Group *g, bool notifyModel) { if (g == m_argumentHints) { emit argumentHintsChanged(); m_updateBestMatchesTimer->start(200); //We have new argument-hints, so we have new best matches return; //Never show argument-hints in the normal completion-list } if (!g->isEmpty) { if (g->filtered.isEmpty()) { // Move to empty group list g->isEmpty = true; int row = m_rowTable.indexOf(g); if (row != -1) { if (hasGroups() && notifyModel) { beginRemoveRows(QModelIndex(), row, row); } m_rowTable.removeAt(row); if (hasGroups() && notifyModel) { endRemoveRows(); } m_emptyGroups.append(g); } else { qCWarning(LOG_KTE) << "Group " << g << " not found in row table!!"; } } } else { if (!g->filtered.isEmpty()) { // Move off empty group list g->isEmpty = false; int row = 0; //Find row where to insert for (int a = 0; a < m_rowTable.count(); a++) { if (g->orderBefore(m_rowTable[a])) { row = a; break; } row = a + 1; } if (notifyModel) { if (hasGroups()) { beginInsertRows(QModelIndex(), row, row); } else { beginInsertRows(QModelIndex(), 0, g->filtered.count()); } } m_rowTable.insert(row, g); if (notifyModel) { endInsertRows(); } m_emptyGroups.removeAll(g); } } } bool KateCompletionModel::indexIsItem(const QModelIndex &index) const { if (!hasGroups()) { return true; } if (groupOfParent(index)) { return true; } return false; } void KateCompletionModel::slotModelReset() { createGroups(); //debugStats(); } void KateCompletionModel::debugStats() { if (!hasGroups()) { qCDebug(LOG_KTE) << "Model groupless, " << m_ungrouped->filtered.count() << " items."; } else { qCDebug(LOG_KTE) << "Model grouped (" << m_rowTable.count() << " groups):"; foreach (Group *g, m_rowTable) { qCDebug(LOG_KTE) << "Group" << g << "count" << g->filtered.count(); } } } bool KateCompletionModel::hasCompletionModel() const { return !m_completionModels.isEmpty(); } void KateCompletionModel::setFilteringEnabled(bool enable) { if (m_filteringEnabled != enable) { m_filteringEnabled = enable; } } void KateCompletionModel::setSortingEnabled(bool enable) { if (m_sortingEnabled != enable) { m_sortingEnabled = enable; beginResetModel(); resort(); endResetModel(); } } void KateCompletionModel::setGroupingEnabled(bool enable) { if (m_groupingEnabled != enable) { m_groupingEnabled = enable; } } void KateCompletionModel::setColumnMergingEnabled(bool enable) { if (m_columnMergingEnabled != enable) { m_columnMergingEnabled = enable; } } bool KateCompletionModel::isColumnMergingEnabled() const { return m_columnMergingEnabled; } bool KateCompletionModel::isGroupingEnabled() const { return m_groupingEnabled; } bool KateCompletionModel::isFilteringEnabled() const { return m_filteringEnabled; } bool KateCompletionModel::isSortingEnabled() const { return m_sortingEnabled; } QString KateCompletionModel::columnName(int column) { switch (column) { case KTextEditor::CodeCompletionModel::Prefix: return i18n("Prefix"); case KTextEditor::CodeCompletionModel::Icon: return i18n("Icon"); case KTextEditor::CodeCompletionModel::Scope: return i18n("Scope"); case KTextEditor::CodeCompletionModel::Name: return i18n("Name"); case KTextEditor::CodeCompletionModel::Arguments: return i18n("Arguments"); case KTextEditor::CodeCompletionModel::Postfix: return i18n("Postfix"); } return QString(); } const QList< QList < int > > &KateCompletionModel::columnMerges() const { return m_columnMerges; } void KateCompletionModel::setColumnMerges(const QList< QList < int > > &columnMerges) { beginResetModel(); m_columnMerges = columnMerges; endResetModel(); } int KateCompletionModel::translateColumn(int sourceColumn) const { if (m_columnMerges.isEmpty()) { return sourceColumn; } /* Debugging - dump column merge list QString columnMerge; foreach (const QList& list, m_columnMerges) { columnMerge += '['; foreach (int column, list) { columnMerge += QString::number(column) + " "; } columnMerge += "] "; } qCDebug(LOG_KTE) << k_funcinfo << columnMerge;*/ int c = 0; foreach (const QList &list, m_columnMerges) { foreach (int column, list) { if (column == sourceColumn) { return c; } } c++; } return -1; } int KateCompletionModel::groupingAttributes(int attribute) const { int ret = 0; if (m_groupingMethod & ScopeType) { if (countBits(attribute & ScopeTypeMask) > 1) { qCWarning(LOG_KTE) << "Invalid completion model metadata: more than one scope type modifier provided."; } if (attribute & KTextEditor::CodeCompletionModel::GlobalScope) { ret |= KTextEditor::CodeCompletionModel::GlobalScope; } else if (attribute & KTextEditor::CodeCompletionModel::NamespaceScope) { ret |= KTextEditor::CodeCompletionModel::NamespaceScope; } else if (attribute & KTextEditor::CodeCompletionModel::LocalScope) { ret |= KTextEditor::CodeCompletionModel::LocalScope; } } if (m_groupingMethod & AccessType) { if (countBits(attribute & AccessTypeMask) > 1) { qCWarning(LOG_KTE) << "Invalid completion model metadata: more than one access type modifier provided."; } if (attribute & KTextEditor::CodeCompletionModel::Public) { ret |= KTextEditor::CodeCompletionModel::Public; } else if (attribute & KTextEditor::CodeCompletionModel::Protected) { ret |= KTextEditor::CodeCompletionModel::Protected; } else if (attribute & KTextEditor::CodeCompletionModel::Private) { ret |= KTextEditor::CodeCompletionModel::Private; } if (accessIncludeStatic() && attribute & KTextEditor::CodeCompletionModel::Static) { ret |= KTextEditor::CodeCompletionModel::Static; } if (accessIncludeConst() && attribute & KTextEditor::CodeCompletionModel::Const) { ret |= KTextEditor::CodeCompletionModel::Const; } } if (m_groupingMethod & ItemType) { if (countBits(attribute & ItemTypeMask) > 1) { qCWarning(LOG_KTE) << "Invalid completion model metadata: more than one item type modifier provided."; } if (attribute & KTextEditor::CodeCompletionModel::Namespace) { ret |= KTextEditor::CodeCompletionModel::Namespace; } else if (attribute & KTextEditor::CodeCompletionModel::Class) { ret |= KTextEditor::CodeCompletionModel::Class; } else if (attribute & KTextEditor::CodeCompletionModel::Struct) { ret |= KTextEditor::CodeCompletionModel::Struct; } else if (attribute & KTextEditor::CodeCompletionModel::Union) { ret |= KTextEditor::CodeCompletionModel::Union; } else if (attribute & KTextEditor::CodeCompletionModel::Function) { ret |= KTextEditor::CodeCompletionModel::Function; } else if (attribute & KTextEditor::CodeCompletionModel::Variable) { ret |= KTextEditor::CodeCompletionModel::Variable; } else if (attribute & KTextEditor::CodeCompletionModel::Enum) { ret |= KTextEditor::CodeCompletionModel::Enum; } /* if (itemIncludeTemplate() && attribute & KTextEditor::CodeCompletionModel::Template) ret |= KTextEditor::CodeCompletionModel::Template;*/ } return ret; } void KateCompletionModel::setGroupingMethod(GroupingMethods m) { m_groupingMethod = m; createGroups(); } bool KateCompletionModel::accessIncludeConst() const { return m_accessConst; } void KateCompletionModel::setAccessIncludeConst(bool include) { if (m_accessConst != include) { m_accessConst = include; if (groupingMethod() & AccessType) { createGroups(); } } } bool KateCompletionModel::accessIncludeStatic() const { return m_accessStatic; } void KateCompletionModel::setAccessIncludeStatic(bool include) { if (m_accessStatic != include) { m_accessStatic = include; if (groupingMethod() & AccessType) { createGroups(); } } } bool KateCompletionModel::accessIncludeSignalSlot() const { return m_accesSignalSlot; } void KateCompletionModel::setAccessIncludeSignalSlot(bool include) { if (m_accesSignalSlot != include) { m_accesSignalSlot = include; if (groupingMethod() & AccessType) { createGroups(); } } } int KateCompletionModel::countBits(int value) const { int count = 0; for (int i = 1; i; i <<= 1) if (i & value) { count++; } return count; } KateCompletionModel::GroupingMethods KateCompletionModel::groupingMethod() const { return m_groupingMethod; } bool KateCompletionModel::isSortingByInheritanceDepth() const { return m_isSortingByInheritance; } void KateCompletionModel::setSortingByInheritanceDepth(bool byInheritance) { m_isSortingByInheritance = byInheritance; } bool KateCompletionModel::isSortingAlphabetical() const { return m_sortingAlphabetical; } Qt::CaseSensitivity KateCompletionModel::sortingCaseSensitivity() const { return m_sortingCaseSensitivity; } KateCompletionModel::Item::Item(bool doInitialMatch, KateCompletionModel *m, const HierarchicalModelHandler &handler, ModelRow sr) : model(m) , m_sourceRow(sr) , matchCompletion(StartsWithMatch) , matchFilters(true) , m_haveExactMatch(false) { inheritanceDepth = handler.getData(CodeCompletionModel::InheritanceDepth, m_sourceRow.second).toInt(); m_unimportant = handler.getData(CodeCompletionModel::UnimportantItemRole, m_sourceRow.second).toBool(); QModelIndex nameSibling = sr.second.sibling(sr.second.row(), CodeCompletionModel::Name); m_nameColumn = nameSibling.data(Qt::DisplayRole).toString(); if (doInitialMatch) { filter(); match(); } } bool KateCompletionModel::Item::operator <(const Item &rhs) const { int ret = 0; //qCDebug(LOG_KTE) << c1 << " c/w " << c2 << " -> " << (model->isSortingReverse() ? ret > 0 : ret < 0) << " (" << ret << ")"; if(m_unimportant && !rhs.m_unimportant){ return false; } if(!m_unimportant && rhs.m_unimportant){ return true; } if (matchCompletion < rhs.matchCompletion) { // enums are ordered in the order items should be displayed return true; } if (matchCompletion > rhs.matchCompletion) { return false; } if (model->isSortingByInheritanceDepth()) { ret = inheritanceDepth - rhs.inheritanceDepth; } if (ret == 0 && model->isSortingAlphabetical()) { // Do not use localeAwareCompare, because it is simply too slow for a list of about 1000 items ret = QString::compare(m_nameColumn, rhs.m_nameColumn, model->sortingCaseSensitivity()); } if (ret == 0) { const QString& filter = rhs.model->currentCompletion(rhs.m_sourceRow.first); if( m_nameColumn.startsWith(filter, Qt::CaseSensitive) ) { return true; } if( rhs.m_nameColumn.startsWith(filter, Qt::CaseSensitive) ) { return false; } // FIXME need to define a better default ordering for multiple model display ret = m_sourceRow.second.row() - rhs.m_sourceRow.second.row(); } return ret < 0; } void KateCompletionModel::Group::addItem(Item i, bool notifyModel) { if (isEmpty) { notifyModel = false; } QModelIndex groupIndex; if (notifyModel) { groupIndex = model->indexForGroup(this); } if (model->isSortingEnabled()) { prefilter.insert(qUpperBound(prefilter.begin(), prefilter.end(), i), i); if (i.isVisible()) { QList::iterator it = qUpperBound(filtered.begin(), filtered.end(), i); uint rowNumber = it - filtered.begin(); if (notifyModel) { model->beginInsertRows(groupIndex, rowNumber, rowNumber); } filtered.insert(it, i); } } else { if (notifyModel) { model->beginInsertRows(groupIndex, prefilter.size(), prefilter.size()); } if (i.isVisible()) { prefilter.append(i); } } if (notifyModel) { model->endInsertRows(); } } bool KateCompletionModel::Group::removeItem(const ModelRow &row) { for (int pi = 0; pi < prefilter.count(); ++pi) if (prefilter[pi].sourceRow() == row) { int index = rowOf(row); if (index != -1) { model->beginRemoveRows(model->indexForGroup(this), index, index); } filtered.removeAt(index); prefilter.removeAt(pi); if (index != -1) { model->endRemoveRows(); } return index != -1; } Q_ASSERT(false); return false; } KateCompletionModel::Group::Group(const QString& title, int attribute, KateCompletionModel *m) : model(m) , attribute(attribute) // ugly hack to add some left margin , title(QLatin1Char(' ') + title) , isEmpty(true) , customSortingKey(-1) { Q_ASSERT(model); } void KateCompletionModel::setSortingAlphabetical(bool alphabetical) { if (m_sortingAlphabetical != alphabetical) { m_sortingAlphabetical = alphabetical; beginResetModel(); resort(); endResetModel(); } } void KateCompletionModel::Group::resort() { qStableSort(filtered.begin(), filtered.end()); model->hideOrShowGroup(this); } void KateCompletionModel::setSortingCaseSensitivity(Qt::CaseSensitivity cs) { if (m_sortingCaseSensitivity != cs) { m_sortingCaseSensitivity = cs; beginResetModel(); resort(); endResetModel(); } } void KateCompletionModel::resort() { foreach (Group *g, m_rowTable) { g->resort(); } foreach (Group *g, m_emptyGroups) { g->resort(); } // call updateBestMatches here, so they are moved to the top again. updateBestMatches(); } bool KateCompletionModel::Item::isValid() const { return model && m_sourceRow.first && m_sourceRow.second.row() >= 0; } void KateCompletionModel::Group::clear() { prefilter.clear(); filtered.clear(); isEmpty = true; } bool KateCompletionModel::filterContextMatchesOnly() const { return m_filterContextMatchesOnly; } void KateCompletionModel::setFilterContextMatchesOnly(bool filter) { if (m_filterContextMatchesOnly != filter) { m_filterContextMatchesOnly = filter; refilter(); } } bool KateCompletionModel::filterByAttribute() const { return m_filterByAttribute; } void KateCompletionModel::setFilterByAttribute(bool filter) { if (m_filterByAttribute == filter) { m_filterByAttribute = filter; refilter(); } } KTextEditor::CodeCompletionModel::CompletionProperties KateCompletionModel::filterAttributes() const { return m_filterAttributes; } void KateCompletionModel::setFilterAttributes(KTextEditor::CodeCompletionModel::CompletionProperties attributes) { if (m_filterAttributes == attributes) { m_filterAttributes = attributes; refilter(); } } int KateCompletionModel::maximumInheritanceDepth() const { return m_maximumInheritanceDepth; } void KateCompletionModel::setMaximumInheritanceDepth(int maxDepth) { if (m_maximumInheritanceDepth != maxDepth) { m_maximumInheritanceDepth = maxDepth; refilter(); } } void KateCompletionModel::refilter() { beginResetModel(); m_ungrouped->refilter(); foreach (Group *g, m_rowTable) if (g != m_argumentHints) { g->refilter(); } foreach (Group *g, m_emptyGroups) if (g != m_argumentHints) { g->refilter(); } updateBestMatches(); clearExpanding(); //We need to do this, or be aware of expanding-widgets while filtering. endResetModel(); } void KateCompletionModel::Group::refilter() { filtered.clear(); foreach (const Item &i, prefilter) if (!i.isFiltered()) { filtered.append(i); } } bool KateCompletionModel::Item::filter() { matchFilters = false; if (model->isFilteringEnabled()) { QModelIndex sourceIndex = m_sourceRow.second.sibling(m_sourceRow.second.row(), CodeCompletionModel::Name); if (model->filterContextMatchesOnly()) { QVariant contextMatch = sourceIndex.data(CodeCompletionModel::MatchQuality); if (contextMatch.canConvert(QVariant::Int) && !contextMatch.toInt()) { return false; } } if (model->filterByAttribute()) { int completionFlags = sourceIndex.data(CodeCompletionModel::CompletionRole).toInt(); if (model->filterAttributes() & completionFlags) { return false; } } if (model->maximumInheritanceDepth() > 0) { int inheritanceDepth = sourceIndex.data(CodeCompletionModel::InheritanceDepth).toInt(); if (inheritanceDepth > model->maximumInheritanceDepth()) { return false; } } } matchFilters = true; return matchFilters; } uint KateCompletionModel::filteredItemCount() const { uint ret = 0; foreach (Group *group, m_rowTable) { ret += group->filtered.size(); } return ret; } bool KateCompletionModel::shouldMatchHideCompletionList() const { // @todo Make this faster bool doHide = false; CodeCompletionModel *hideModel = nullptr; foreach (Group *group, m_rowTable) foreach (const Item &item, group->filtered) if (item.haveExactMatch()) { KTextEditor::CodeCompletionModelControllerInterface *iface3 = dynamic_cast(item.sourceRow().first); bool hide = false; if (!iface3) { hide = true; } if (iface3 && iface3->matchingItem(item.sourceRow().second) == KTextEditor::CodeCompletionModelControllerInterface::HideListIfAutomaticInvocation) { hide = true; } if (hide) { doHide = true; hideModel = item.sourceRow().first; } } if (doHide) { // Check if all other visible items are from the same model foreach (Group *group, m_rowTable) foreach (const Item &item, group->filtered) if (item.sourceRow().first != hideModel) { return false; } } return doHide; } static inline bool matchesAbbreviationHelper(const QString &word, const QString &typed, const QVarLengthArray &offsets, int &depth, int atWord = -1, int i = 0) { int atLetter = 1; for (; i < typed.size(); i++) { const QChar c = typed.at(i).toLower(); bool haveNextWord = offsets.size() > atWord + 1; bool canCompare = atWord != -1 && word.size() > offsets.at(atWord) + atLetter; if (canCompare && c == word.at(offsets.at(atWord) + atLetter).toLower()) { // the typed letter matches a letter after the current word beginning if (! haveNextWord || c != word.at(offsets.at(atWord + 1)).toLower()) { // good, simple case, no conflict atLetter += 1; continue; } // For maliciously crafted data, the code used here theoretically can have very high // complexity. Thus ensure we don't run into this case, by limiting the amount of branches // we walk through to 128. depth++; if (depth > 128) { return false; } // the letter matches both the next word beginning and the next character in the word if (haveNextWord && matchesAbbreviationHelper(word, typed, offsets, depth, atWord + 1, i + 1)) { // resolving the conflict by taking the next word's first character worked, fine return true; } // otherwise, continue by taking the next letter in the current word. atLetter += 1; continue; } else if (haveNextWord && c == word.at(offsets.at(atWord + 1)).toLower()) { // the typed letter matches the next word beginning atWord++; atLetter = 1; continue; } // no match return false; } // all characters of the typed word were matched return true; } bool KateCompletionModel::matchesAbbreviation(const QString &word, const QString &typed) { // A mismatch is very likely for random even for the first letter, // thus this optimization makes sense. if (word.at(0).toLower() != typed.at(0).toLower()) { return false; } // First, check if all letters are contained in the word in the right order. int atLetter = 0; foreach (const QChar c, typed) { while (c.toLower() != word.at(atLetter).toLower()) { atLetter += 1; if (atLetter >= word.size()) { return false; } } } bool haveUnderscore = true; QVarLengthArray offsets; // We want to make "KComplM" match "KateCompletionModel"; this means we need // to allow parts of the typed text to be not part of the actual abbreviation, // which consists only of the uppercased / underscored letters (so "KCM" in this case). // However it might be ambigous whether a letter is part of such a word or part of // the following abbreviation, so we need to find all possible word offsets first, // then compare. for (int i = 0; i < word.size(); i++) { const QChar c = word.at(i); if (c == QLatin1Char('_')) { haveUnderscore = true; } else if (haveUnderscore || c.isUpper()) { offsets.append(i); haveUnderscore = false; } } int depth = 0; return matchesAbbreviationHelper(word, typed, offsets, depth); } static inline bool containsAtWordBeginning(const QString &word, const QString &typed, Qt::CaseSensitivity caseSensitive) { for (int i = 1; i < word.size(); i++) { // The current position is a word beginning if the previous character was an underscore // or if the current character is uppercase. Subsequent uppercase characters do not count, // to handle the special case of UPPER_CASE_VARS properly. const QChar c = word.at(i); const QChar prev = word.at(i - 1); if (!(prev == QLatin1Char('_') || (c.isUpper() && !prev.isUpper()))) { continue; } if (word.midRef(i).startsWith(typed, caseSensitive)) { return true; } } return false; } KateCompletionModel::Item::MatchType KateCompletionModel::Item::match() { QString match = model->currentCompletion(m_sourceRow.first); m_haveExactMatch = false; // Hehe, everything matches nothing! (ie. everything matches a blank string) if (match.isEmpty()) { return PerfectMatch; } if (m_nameColumn.isEmpty()) { return NoMatch; } matchCompletion = (m_nameColumn.startsWith(match, model->matchCaseSensitivity()) ? StartsWithMatch : NoMatch); if (matchCompletion == NoMatch) { // if no match, try for "contains" // Only match when the occurence is at a "word" beginning, marked by // an underscore or a capital. So Foo matches BarFoo and Bar_Foo, but not barfoo. // Starting at 1 saves looking at the beginning of the word, that was already checked above. if (containsAtWordBeginning(m_nameColumn, match, model->matchCaseSensitivity())) { matchCompletion = ContainsMatch; } } if (matchCompletion == NoMatch && !m_nameColumn.isEmpty() && !match.isEmpty()) { // if still no match, try abbreviation matching if (matchesAbbreviation(m_nameColumn, match)) { matchCompletion = AbbreviationMatch; } } if (matchCompletion && match.length() == m_nameColumn.length()) { matchCompletion = PerfectMatch; m_haveExactMatch = true; } return matchCompletion; } QString KateCompletionModel::propertyName(KTextEditor::CodeCompletionModel::CompletionProperty property) { switch (property) { case CodeCompletionModel::Public: return i18n("Public"); case CodeCompletionModel::Protected: return i18n("Protected"); case CodeCompletionModel::Private: return i18n("Private"); case CodeCompletionModel::Static: return i18n("Static"); case CodeCompletionModel::Const: return i18n("Constant"); case CodeCompletionModel::Namespace: return i18n("Namespace"); case CodeCompletionModel::Class: return i18n("Class"); case CodeCompletionModel::Struct: return i18n("Struct"); case CodeCompletionModel::Union: return i18n("Union"); case CodeCompletionModel::Function: return i18n("Function"); case CodeCompletionModel::Variable: return i18n("Variable"); case CodeCompletionModel::Enum: return i18n("Enumeration"); case CodeCompletionModel::Template: return i18n("Template"); case CodeCompletionModel::Virtual: return i18n("Virtual"); case CodeCompletionModel::Override: return i18n("Override"); case CodeCompletionModel::Inline: return i18n("Inline"); case CodeCompletionModel::Friend: return i18n("Friend"); case CodeCompletionModel::Signal: return i18n("Signal"); case CodeCompletionModel::Slot: return i18n("Slot"); case CodeCompletionModel::LocalScope: return i18n("Local Scope"); case CodeCompletionModel::NamespaceScope: return i18n("Namespace Scope"); case CodeCompletionModel::GlobalScope: return i18n("Global Scope"); default: return i18n("Unknown Property"); } } bool KateCompletionModel::Item::isVisible() const { return matchCompletion && matchFilters; } bool KateCompletionModel::Item::isFiltered() const { return !matchFilters; } bool KateCompletionModel::Item::isMatching() const { return matchFilters; } const KateCompletionModel::ModelRow &KateCompletionModel::Item::sourceRow() const { return m_sourceRow; } QString KateCompletionModel::currentCompletion(KTextEditor::CodeCompletionModel *model) const { return m_currentMatch.value(model); } Qt::CaseSensitivity KateCompletionModel::matchCaseSensitivity() const { return m_matchCaseSensitivity; } void KateCompletionModel::addCompletionModel(KTextEditor::CodeCompletionModel *model) { if (m_completionModels.contains(model)) { return; } m_completionModels.append(model); connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(slotRowsInserted(QModelIndex,int,int))); connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), SLOT(slotRowsRemoved(QModelIndex,int,int))); connect(model, SIGNAL(modelReset()), SLOT(slotModelReset())); // This performs the reset createGroups(); } void KateCompletionModel::setCompletionModel(KTextEditor::CodeCompletionModel *model) { clearCompletionModels(); addCompletionModel(model); } void KateCompletionModel::setCompletionModels(const QList &models) { //if (m_completionModels == models) //return; clearCompletionModels(); m_completionModels = models; foreach (KTextEditor::CodeCompletionModel *model, models) { connect(model, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(slotRowsInserted(QModelIndex,int,int))); connect(model, SIGNAL(rowsRemoved(QModelIndex,int,int)), SLOT(slotRowsRemoved(QModelIndex,int,int))); connect(model, SIGNAL(modelReset()), SLOT(slotModelReset())); } // This performs the reset createGroups(); } QList< KTextEditor::CodeCompletionModel * > KateCompletionModel::completionModels() const { return m_completionModels; } void KateCompletionModel::removeCompletionModel(CodeCompletionModel *model) { if (!model || !m_completionModels.contains(model)) { return; } beginResetModel(); m_currentMatch.remove(model); clearGroups(); model->disconnect(this); m_completionModels.removeAll(model); endResetModel(); if (!m_completionModels.isEmpty()) { // This performs the reset createGroups(); } } void KateCompletionModel::makeGroupItemsUnique(bool onlyFiltered) { struct FilterItems { FilterItems(KateCompletionModel &model, const QVector &needShadowing) : m_model(model), m_needShadowing(needShadowing) { } QHash had; KateCompletionModel &m_model; const QVector< KTextEditor::CodeCompletionModel * > m_needShadowing; void filter(QList &items) { QList temp; foreach (const Item &item, items) { QHash::const_iterator it = had.constFind(item.name()); if (it != had.constEnd() && *it != item.sourceRow().first && m_needShadowing.contains(item.sourceRow().first)) { continue; } had.insert(item.name(), item.sourceRow().first); temp.push_back(item); } items = temp; } void filter(Group *group, bool onlyFiltered) { if (group->prefilter.size() == group->filtered.size()) { // Filter only once filter(group->filtered); if (!onlyFiltered) { group->prefilter = group->filtered; } } else { // Must filter twice filter(group->filtered); if (!onlyFiltered) { filter(group->prefilter); } } if (group->filtered.isEmpty()) { m_model.hideOrShowGroup(group); } } }; QVector needShadowing; foreach (KTextEditor::CodeCompletionModel *model, m_completionModels) { KTextEditor::CodeCompletionModelControllerInterface *v4 = dynamic_cast(model); if (v4 && v4->shouldHideItemsWithEqualNames()) { needShadowing.push_back(model); } } if (needShadowing.isEmpty()) { return; } FilterItems filter(*this, needShadowing); filter.filter(m_ungrouped, onlyFiltered); foreach (Group *group, m_rowTable) { filter.filter(group, onlyFiltered); } } //Updates the best-matches group void KateCompletionModel::updateBestMatches() { int maxMatches = 300; //We cannot do too many operations here, because they are all executed whenever a character is added. Would be nice if we could split the operations up somewhat using a timer. m_updateBestMatchesTimer->stop(); //Maps match-qualities to ModelRows paired together with the BestMatchesCount returned by the items. typedef QMultiMap > BestMatchMap; BestMatchMap matches; if (!hasGroups()) { //If there is no grouping, just change the order of the items, moving the best matching ones to the front QMultiMap rowsForQuality; int row = 0; foreach (const Item &item, m_ungrouped->filtered) { ModelRow source = item.sourceRow(); QVariant v = source.second.data(CodeCompletionModel::BestMatchesCount); if (v.type() == QVariant::Int && v.toInt() > 0) { int quality = contextMatchQuality(source); if (quality > 0) { rowsForQuality.insert(quality, row); } } ++row; --maxMatches; if (maxMatches < 0) { break; } } if (!rowsForQuality.isEmpty()) { //Rewrite m_ungrouped->filtered in a new order QSet movedToFront; QList newFiltered; for (QMultiMap::const_iterator it = rowsForQuality.constBegin(); it != rowsForQuality.constEnd(); ++it) { newFiltered.prepend(m_ungrouped->filtered[it.value()]); movedToFront.insert(it.value()); } { - uint size = m_ungrouped->filtered.size(); - for (uint a = 0; a < size; ++a) + int size = m_ungrouped->filtered.size(); + for (int a = 0; a < size; ++a) if (!movedToFront.contains(a)) { newFiltered.append(m_ungrouped->filtered[a]); } } m_ungrouped->filtered = newFiltered; } return; } ///@todo Cache the CodeCompletionModel::BestMatchesCount foreach (Group *g, m_rowTable) { if (g == m_bestMatches) { continue; } for (int a = 0; a < g->filtered.size(); a++) { ModelRow source = g->filtered[a].sourceRow(); QVariant v = source.second.data(CodeCompletionModel::BestMatchesCount); if (v.type() == QVariant::Int && v.toInt() > 0) { //Return the best match with any of the argument-hints int quality = contextMatchQuality(source); if (quality > 0) { matches.insert(quality, qMakePair(v.toInt(), g->filtered[a].sourceRow())); } --maxMatches; } if (maxMatches < 0) { break; } } if (maxMatches < 0) { break; } } //Now choose how many of the matches will be taken. This is done with the rule: //The count of shown best-matches should equal the average count of their BestMatchesCounts int cnt = 0; int matchesSum = 0; BestMatchMap::const_iterator it = matches.constEnd(); while (it != matches.constBegin()) { --it; ++cnt; matchesSum += (*it).first; if (cnt > matchesSum / cnt) { break; } } m_bestMatches->filtered.clear(); it = matches.constEnd(); while (it != matches.constBegin() && cnt > 0) { --it; --cnt; m_bestMatches->filtered.append(Item(true, this, HierarchicalModelHandler((*it).second.first), (*it).second)); } hideOrShowGroup(m_bestMatches); } void KateCompletionModel::rowSelected(const QModelIndex &row) { ExpandingWidgetModel::rowSelected(row); ///@todo delay this int rc = widget()->argumentHintModel()->rowCount(QModelIndex()); if (rc == 0) { return; } //For now, simply update the whole column 0 QModelIndex start = widget()->argumentHintModel()->index(0, 0); QModelIndex end = widget()->argumentHintModel()->index(rc - 1, 0); widget()->argumentHintModel()->emitDataChanged(start, end); } void KateCompletionModel::clearCompletionModels() { if (m_completionModels.isEmpty()) { return; } beginResetModel(); foreach (CodeCompletionModel *model, m_completionModels) { model->disconnect(this); } m_completionModels.clear(); m_currentMatch.clear(); clearGroups(); endResetModel(); } diff --git a/src/completion/katecompletionwidget.h b/src/completion/katecompletionwidget.h index c56f9082..3265e6ff 100644 --- a/src/completion/katecompletionwidget.h +++ b/src/completion/katecompletionwidget.h @@ -1,248 +1,248 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2005-2006 Hamish Rodda * Copyright (C) 2007-2008 David Nolden * * 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 KATECOMPLETIONWIDGET_H #define KATECOMPLETIONWIDGET_H #include #include #include #include #include #include class QToolButton; class QPushButton; class QLabel; class QTimer; namespace KTextEditor { class ViewPrivate; } class KateCompletionModel; class KateCompletionTree; class KateArgumentHintTree; class KateArgumentHintModel; namespace KTextEditor { class EmbeddedWidgetInterface; } /** * This is the code completion's main widget, and also contains the * core interface logic. * * @author Hamish Rodda */ class KTEXTEDITOR_EXPORT KateCompletionWidget : public QFrame { Q_OBJECT public: explicit KateCompletionWidget(KTextEditor::ViewPrivate *parent); - ~KateCompletionWidget(); + ~KateCompletionWidget() Q_DECL_OVERRIDE; KTextEditor::ViewPrivate *view() const; KateCompletionTree *treeView() const; bool isCompletionActive() const; void startCompletion(KTextEditor::CodeCompletionModel::InvocationType invocationType, const QList &models = QList()); void startCompletion(const KTextEditor::Range &word, KTextEditor::CodeCompletionModel *model, KTextEditor::CodeCompletionModel::InvocationType invocationType = KTextEditor::CodeCompletionModel::ManualInvocation); void startCompletion(const KTextEditor::Range &word, const QList &models = QList(), KTextEditor::CodeCompletionModel::InvocationType invocationType = KTextEditor::CodeCompletionModel::ManualInvocation); void userInvokedCompletion(); public Q_SLOTS: //Executed when return is pressed while completion is active. void execute(); void cursorDown(); void cursorUp(); public: void tab(bool shift); ///Returns whether the current item was expanded/unexpanded bool toggleExpanded(bool forceExpand = false, bool forceUnExpand = false); const KateCompletionModel *model() const; KateCompletionModel *model(); void registerCompletionModel(KTextEditor::CodeCompletionModel *model); void unregisterCompletionModel(KTextEditor::CodeCompletionModel *model); bool isCompletionModelRegistered(KTextEditor::CodeCompletionModel *model) const; int automaticInvocationDelay() const; void setAutomaticInvocationDelay(int delay); struct CompletionRange { CompletionRange() { } explicit CompletionRange(KTextEditor::MovingRange *r) : range(r) { } bool operator==(const CompletionRange &rhs) const { return range->toRange() == rhs.range->toRange(); } KTextEditor::MovingRange *range = nullptr; //Whenever the cursor goes before this position, the completion is stopped, unless it is invalid. KTextEditor::Cursor leftBoundary; }; KTextEditor::MovingRange *completionRange(KTextEditor::CodeCompletionModel *model = nullptr) const; QMap completionRanges() const; // Navigation void pageDown(); void pageUp(); void top(); void bottom(); QWidget *currentEmbeddedWidget(); bool canExpandCurrentItem() const; bool canCollapseCurrentItem() const; void setCurrentItemExpanded(bool); //Returns true if a screen border has been hit bool updatePosition(bool force = false); bool eventFilter(QObject *watched, QEvent *event) Q_DECL_OVERRIDE; KateArgumentHintTree *argumentHintTree() const; KateArgumentHintModel *argumentHintModel() const; ///Called by KateViewInternal, because we need the specific information from the event. void updateHeight(); public Q_SLOTS: void waitForModelReset(); void abortCompletion(); void showConfig(); /* void viewFocusIn(); void viewFocusOut();*/ void updatePositionSlot(); void automaticInvocation(); /* void updateFocus();*/ void argumentHintsChanged(bool hasContent); bool navigateUp(); bool navigateDown(); bool navigateLeft(); bool navigateRight(); bool navigateAccept(); bool navigateBack(); bool hadNavigation() const; void resetHadNavigation(); protected: void showEvent(QShowEvent *event) Q_DECL_OVERRIDE; void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE; void moveEvent(QMoveEvent *event) Q_DECL_OVERRIDE; void focusOutEvent(QFocusEvent * event) Q_DECL_OVERRIDE; private Q_SLOTS: void completionModelReset(); void modelDestroyed(QObject *model); void modelContentChanged(); void cursorPositionChanged(); void modelReset(); void rowsInserted(const QModelIndex &parent, int row, int rowEnd); void viewFocusOut(); void wrapLine(const KTextEditor::Cursor &position); void unwrapLine(int line); void insertText(const KTextEditor::Cursor &position, const QString &text); void removeText(const KTextEditor::Range &range); private: void updateAndShow(); void updateArgumentHintGeometry(); QModelIndex selectedIndex() const; void clear(); //Switch cursor between argument-hint list / completion-list void switchList(); KTextEditor::Range determineRange() const; void completionRangeChanged(KTextEditor::CodeCompletionModel *, const KTextEditor::Range &word); void deleteCompletionRanges(); QList m_sourceModels; KateCompletionModel *m_presentationModel; QMap m_completionRanges; QSet m_waitingForReset; KTextEditor::Cursor m_lastCursorPosition; KateCompletionTree *m_entryList; KateArgumentHintModel *m_argumentHintModel; KateArgumentHintTree *m_argumentHintTree; QTimer *m_automaticInvocationTimer; //QTimer* m_updateFocusTimer; QWidget *m_statusBar; QToolButton *m_sortButton; QLabel *m_sortText; QToolButton *m_filterButton; QLabel *m_filterText; QPushButton *m_configButton; KTextEditor::Cursor m_automaticInvocationAt; QString m_automaticInvocationLine; int m_automaticInvocationDelay; bool m_filterInstalled; class KateCompletionConfig *m_configWidget; bool m_lastInsertionByUser; bool m_inCompletionList; //Are we in the completion-list? If not, we're in the argument-hint list bool m_isSuspended; bool m_dontShowArgumentHints; //Used temporarily to prevent flashing bool m_needShow; bool m_hadCompletionNavigation; bool m_haveExactMatch; bool m_noAutoHide; /** * is a completion edit ongoing? */ bool m_completionEditRunning; int m_expandedAddedHeightBase; KTextEditor::CodeCompletionModel::InvocationType m_lastInvocationType; }; #endif diff --git a/src/completion/katewordcompletion.h b/src/completion/katewordcompletion.h index 8f6a6fd8..047cdcf4 100644 --- a/src/completion/katewordcompletion.h +++ b/src/completion/katewordcompletion.h @@ -1,114 +1,114 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2003 Anders Lund * Copyright (C) 2010 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 _KateWordCompletion_h_ #define _KateWordCompletion_h_ #include #include #include #include #include #include #include #include "katepartdebug.h" #include class KTEXTEDITOR_EXPORT KateWordCompletionModel : public KTextEditor::CodeCompletionModel, public KTextEditor::CodeCompletionModelControllerInterface { Q_OBJECT Q_INTERFACES(KTextEditor::CodeCompletionModelControllerInterface) public: explicit KateWordCompletionModel(QObject *parent); - ~KateWordCompletionModel(); + ~KateWordCompletionModel() Q_DECL_OVERRIDE; /** * This function is responsible to generating / updating the list of current * completions. The default implementation does nothing. * * When implementing this function, remember to call setRowCount() (or implement * rowCount()), and to generate the appropriate change notifications (for instance * by calling QAbstractItemModel::reset()). * @param view The view to generate completions for * @param range The range of text to generate completions for * */ void completionInvoked(KTextEditor::View *view, const KTextEditor::Range &range, InvocationType invocationType) Q_DECL_OVERRIDE; bool shouldStartCompletion(KTextEditor::View *view, const QString &insertedText, bool userInsertion, const KTextEditor::Cursor &position) Q_DECL_OVERRIDE; bool shouldAbortCompletion(KTextEditor::View *view, const KTextEditor::Range &range, const QString ¤tCompletion) Q_DECL_OVERRIDE; void saveMatches(KTextEditor::View *view, const KTextEditor::Range &range); int rowCount(const QModelIndex &parent) const Q_DECL_OVERRIDE; QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE; MatchReaction matchingItem(const QModelIndex &matched) Q_DECL_OVERRIDE; KTextEditor::Range completionRange(KTextEditor::View *view, const KTextEditor::Cursor &position) Q_DECL_OVERRIDE; bool shouldHideItemsWithEqualNames() const Q_DECL_OVERRIDE; QStringList allMatches(KTextEditor::View *view, const KTextEditor::Range &range) const; void executeCompletionItem (KTextEditor::View *view, const KTextEditor::Range &word, const QModelIndex &index) const Q_DECL_OVERRIDE; private: QStringList m_matches; bool m_automatic; }; class KateWordCompletionView : public QObject { Q_OBJECT public: KateWordCompletionView(KTextEditor::View *view, KActionCollection *ac); ~KateWordCompletionView(); private Q_SLOTS: void completeBackwards(); void completeForwards(); void slotCursorMoved(); void shellComplete(); void popupCompletionList(); private: void complete(bool fw = true); QString word() const; KTextEditor::Range range() const; QString findLongestUnique(const QStringList &matches, int lead) const; KTextEditor::View *m_view; KateWordCompletionModel *m_dWCompletionModel; struct KateWordCompletionViewPrivate *d; }; #endif // _DocWordCompletionPlugin_h_ diff --git a/src/document/katebuffer.h b/src/document/katebuffer.h index d96003a8..1bbe7c22 100644 --- a/src/document/katebuffer.h +++ b/src/document/katebuffer.h @@ -1,294 +1,294 @@ /* This file is part of the KDE libraries Copyright (c) 2000 Waldo Bastian Copyright (C) 2002-2004 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_BUFFER_H__ #define __KATE_BUFFER_H__ #include "katetextbuffer.h" #include #include class KateLineInfo; namespace KTextEditor { class DocumentPrivate; } class KateHighlighting; /** * The KateBuffer class maintains a collections of lines. * * @author Waldo Bastian * @author Christoph Cullmann */ class KTEXTEDITOR_EXPORT KateBuffer : public Kate::TextBuffer { Q_OBJECT public: /** * Create an empty buffer. * @param doc parent document */ explicit KateBuffer(KTextEditor::DocumentPrivate *doc); /** * Goodbye buffer */ - ~KateBuffer(); + ~KateBuffer() Q_DECL_OVERRIDE; public: /** * start some editing action */ void editStart(); /** * finish some editing action */ void editEnd(); /** * were there changes in the current running * editing session? * @return changes done? */ inline bool editChanged() const { return editingChangedBuffer(); } /** * dirty lines start * @return start line */ inline int editTagStart() const { return editingMinimalLineChanged(); } /** * dirty lines end * @return end line */ inline int editTagEnd() const { return editingMaximalLineChanged(); } /** * line inserted/removed? * @return line inserted/removed? */ inline bool editTagFrom() const { return editingChangedNumberOfLines() != 0; } public: /** * Clear the buffer. */ void clear() Q_DECL_OVERRIDE; /** * Open a file, use the given filename * @param m_file filename to open * @param enforceTextCodec enforce to use only the set text codec * @return success */ bool openFile(const QString &m_file, bool enforceTextCodec); /** * Did encoding errors occur on load? * @return encoding errors occurred on load? */ bool brokenEncoding() const { return m_brokenEncoding; } /** * Too long lines wrapped on load? * @return too long lines wrapped on load? */ bool tooLongLinesWrapped() const { return m_tooLongLinesWrapped; } int longestLineLoaded() const { return m_longestLineLoaded; } /** * Can the current codec handle all chars * @return chars can be encoded */ bool canEncode(); /** * Save the buffer to a file, use the given filename + codec + end of line chars (internal use of qtextstream) * @param m_file filename to save to * @return success */ bool saveFile(const QString &m_file); public: /** * Return line @p lineno. * Highlighting of returned line might be out-dated, which may be sufficient * for pure text manipulation functions, like search/replace. * If you require highlighting to be up to date, call @ref ensureHighlighted * prior to this method. */ inline Kate::TextLine plainLine(int lineno) { if (lineno < 0 || lineno >= lines()) { return Kate::TextLine(); } return line(lineno); } /** * Update highlighting of given line @p line, if needed. * If @p line is already highlighted, this function does nothing. * If @p line is not highlighted, all lines up to line + lookAhead * are highlighted. * @param lookAhead also highlight these following lines */ void ensureHighlighted(int line, int lookAhead = 64); /** * Return the total number of lines in the buffer. */ inline int count() const { return lines(); } /** * Unwrap given line. * @param line line to unwrap */ void unwrapLine(int line) Q_DECL_OVERRIDE; /** * Wrap line at given cursor position. * @param position line/column as cursor where to wrap */ void wrapLine(const KTextEditor::Cursor &position) Q_DECL_OVERRIDE; public: inline int tabWidth() const { return m_tabWidth; } public: void setTabWidth(int w); /** * Use @p highlight for highlighting * * @p highlight may be 0 in which case highlighting * will be disabled. */ void setHighlight(int hlMode); KateHighlighting *highlight() { return m_highlight; } /** * Invalidate highlighting of whole buffer. */ void invalidateHighlighting(); /** * For a given line, compute the folding range that starts there * to be used to fold e.g. from the icon border * @param startLine start line * @return folding range starting at the given line or invalid range */ KTextEditor::Range computeFoldingRangeForStartLine(int startLine); private: /** * Highlight information needs to be updated. * * @param from first line in range * @param to last line in range * @param invalidate should the rehighlighted lines be tagged? */ void doHighlight(int from, int to, bool invalidate); Q_SIGNALS: /** * Emitted when the highlighting of a certain range has * changed. */ void tagLines(int start, int end); void respellCheckBlock(int start, int end); private: /** * document we belong to */ KTextEditor::DocumentPrivate *const m_doc; /** * file loaded with encoding problems? */ bool m_brokenEncoding; /** * too long lines wrapped on load? */ bool m_tooLongLinesWrapped; /** * length of the longest line loaded */ int m_longestLineLoaded; /** * current highlighting mode or 0 */ KateHighlighting *m_highlight; // for the scrapty indent sensitive langs int m_tabWidth; /** * last line with valid highlighting */ int m_lineHighlighted; /** * number of dynamic contexts causing a full invalidation */ int m_maxDynamicContexts; }; #endif diff --git a/src/document/katedocument.cpp b/src/document/katedocument.cpp index 9cda5cd0..19d7f4cb 100644 --- a/src/document/katedocument.cpp +++ b/src/document/katedocument.cpp @@ -1,6019 +1,6019 @@ /* This file is part of the KDE libraries Copyright (C) 2001-2004 Christoph Cullmann Copyright (C) 2001 Joseph Wenninger Copyright (C) 1999 Jochen Wilhelmy Copyright (C) 2006 Hamish Rodda Copyright (C) 2007 Mirko Stocker Copyright (C) 2009-2010 Michel Ludwig Copyright (C) 2013 Gerald Senarclens de Grancy 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 02111-13020, USA. */ //BEGIN includes #include "config.h" #include "katedocument.h" #include "kateglobal.h" #include "katedialogs.h" #include "katehighlight.h" #include "kateview.h" #include "kateautoindent.h" #include "katetextline.h" #include "katehighlighthelpers.h" #include "katerenderer.h" #include "kateregexp.h" #include "kateplaintextsearch.h" #include "kateregexpsearch.h" #include "kateconfig.h" #include "katemodemanager.h" #include "kateschema.h" #include "katebuffer.h" #include "kateundomanager.h" #include "spellcheck/prefixstore.h" #include "spellcheck/ontheflycheck.h" #include "spellcheck/spellcheck.h" #include "katescriptmanager.h" #include "kateswapfile.h" #include "katepartdebug.h" #include "printing/kateprinter.h" #include "kateabstractinputmode.h" #include "katetemplatehandler.h" #if EDITORCONFIG_FOUND #include "editorconfig.h" #endif #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 #if LIBGIT2_FOUND #include #include #include #endif //END includes #if 0 #define EDIT_DEBUG qCDebug(LOG_KTE) #else #define EDIT_DEBUG if (0) qCDebug(LOG_KTE) #endif template static int indexOf(const std::initializer_list & list, const E& entry) { auto it = std::find(list.begin(), list.end(), entry); return it == list.end() ? -1 : std::distance(list.begin(), it); } template static bool contains(const std::initializer_list & list, const E& entry) { return indexOf(list, entry) >= 0; } static inline QChar matchingStartBracket(QChar c, bool withQuotes) { switch (c.toLatin1()) { case '}': return QLatin1Char('{'); case ']': return QLatin1Char('['); case ')': return QLatin1Char('('); case '\'': return withQuotes ? QLatin1Char('\'') : QChar(); case '"': return withQuotes ? QLatin1Char('"') : QChar(); } return QChar(); } static inline QChar matchingEndBracket(QChar c, bool withQuotes) { switch (c.toLatin1()) { case '{': return QLatin1Char('}'); case '[': return QLatin1Char(']'); case '(': return QLatin1Char(')'); case '\'': return withQuotes ? QLatin1Char('\'') : QChar(); case '"': return withQuotes ? QLatin1Char('"') : QChar(); } return QChar(); } static inline QChar matchingBracket(QChar c, bool withQuotes) { QChar bracket = matchingStartBracket(c, withQuotes); if (bracket.isNull()) { bracket = matchingEndBracket(c, false); } return bracket; } static inline bool isStartBracket(const QChar &c) { return ! matchingEndBracket(c, false).isNull(); } static inline bool isEndBracket(const QChar &c) { return ! matchingStartBracket(c, false).isNull(); } static inline bool isBracket(const QChar &c) { return isStartBracket(c) || isEndBracket(c); } /** * normalize given url * @param url input url * @return normalized url */ static QUrl normalizeUrl (const QUrl &url) { /** * only normalize local urls */ if (url.isEmpty() || !url.isLocalFile()) return url; /** * don't normalize if not existing! * canonicalFilePath won't work! */ const QString normalizedUrl(QFileInfo(url.toLocalFile()).canonicalFilePath()); if (normalizedUrl.isEmpty()) return url; /** * else: use canonicalFilePath to normalize */ return QUrl::fromLocalFile(normalizedUrl); } //BEGIN d'tor, c'tor // // KTextEditor::DocumentPrivate Constructor // KTextEditor::DocumentPrivate::DocumentPrivate(bool bSingleViewMode, bool bReadOnly, QWidget *parentWidget, QObject *parent) : KTextEditor::Document (this, parent), m_bSingleViewMode(bSingleViewMode), m_bReadOnly(bReadOnly), m_undoManager(new KateUndoManager(this)), m_buffer(new KateBuffer(this)), m_indenter(new KateAutoIndent(this)), m_docName(QStringLiteral("need init")), m_fileType(QStringLiteral("Normal")), m_config(new KateDocumentConfig(this)) { /** * no plugins from kparts here */ setPluginLoadingMode (DoNotLoadPlugins); /** * pass on our component data, do this after plugin loading is off */ setComponentData(KTextEditor::EditorPrivate::self()->aboutData()); /** * avoid spamming plasma and other window managers with progress dialogs * we show such stuff inline in the views! */ setProgressInfoEnabled(false); // register doc at factory KTextEditor::EditorPrivate::self()->registerDocument(this); // normal hl m_buffer->setHighlight(0); // swap file m_swapfile = (config()->swapFileMode() == KateDocumentConfig::DisableSwapFile) ? nullptr : new Kate::SwapFile(this); // important, fill in the config into the indenter we use... m_indenter->updateConfig(); // some nice signals from the buffer connect(m_buffer, SIGNAL(tagLines(int,int)), this, SLOT(tagLines(int,int))); // if the user changes the highlight with the dialog, notify the doc connect(KateHlManager::self(), SIGNAL(changed()), SLOT(internalHlChanged())); // signals for mod on hd connect(KTextEditor::EditorPrivate::self()->dirWatch(), SIGNAL(dirty(QString)), this, SLOT(slotModOnHdDirty(QString))); connect(KTextEditor::EditorPrivate::self()->dirWatch(), SIGNAL(created(QString)), this, SLOT(slotModOnHdCreated(QString))); connect(KTextEditor::EditorPrivate::self()->dirWatch(), SIGNAL(deleted(QString)), this, SLOT(slotModOnHdDeleted(QString))); /** * singleshot timer to handle updates of mod on hd state delayed */ m_modOnHdTimer.setSingleShot(true); m_modOnHdTimer.setInterval(200); connect(&m_modOnHdTimer, SIGNAL(timeout()), this, SLOT(slotDelayedHandleModOnHd())); /** * load handling * this is needed to ensure we signal the user if a file ist still loading * and to disallow him to edit in that time */ connect(this, SIGNAL(started(KIO::Job*)), this, SLOT(slotStarted(KIO::Job*))); connect(this, SIGNAL(completed()), this, SLOT(slotCompleted())); connect(this, SIGNAL(canceled(QString)), this, SLOT(slotCanceled())); connect(this, SIGNAL(urlChanged(QUrl)), this, SLOT(slotUrlChanged(QUrl))); // update doc name updateDocName(); // if single view mode, like in the konqui embedding, create a default view ;) // be lazy, only create it now, if any parentWidget is given, otherwise widget() // will create it on demand... if (m_bSingleViewMode && parentWidget) { KTextEditor::View *view = (KTextEditor::View *)createView(parentWidget); insertChildClient(view); view->setContextMenu(view->defaultContextMenu()); setWidget(view); } connect(m_undoManager, SIGNAL(undoChanged()), this, SIGNAL(undoChanged())); connect(m_undoManager, SIGNAL(undoStart(KTextEditor::Document*)), this, SIGNAL(editingStarted(KTextEditor::Document*))); connect(m_undoManager, SIGNAL(undoEnd(KTextEditor::Document*)), this, SIGNAL(editingFinished(KTextEditor::Document*))); connect(m_undoManager, SIGNAL(redoStart(KTextEditor::Document*)), this, SIGNAL(editingStarted(KTextEditor::Document*))); connect(m_undoManager, SIGNAL(redoEnd(KTextEditor::Document*)), this, SIGNAL(editingFinished(KTextEditor::Document*))); connect(this, SIGNAL(sigQueryClose(bool*,bool*)), this, SLOT(slotQueryClose_save(bool*,bool*))); connect(this, &KTextEditor::DocumentPrivate::textRemoved, this, &KTextEditor::DocumentPrivate::saveEditingPositions); connect(this, &KTextEditor::DocumentPrivate::textInserted, this, &KTextEditor::DocumentPrivate::saveEditingPositions); connect(this, SIGNAL(aboutToInvalidateMovingInterfaceContent(KTextEditor::Document*)), this, SLOT(clearEditingPosStack())); onTheFlySpellCheckingEnabled(config()->onTheFlySpellCheck()); } // // KTextEditor::DocumentPrivate Destructor // KTextEditor::DocumentPrivate::~DocumentPrivate() { // delete pending mod-on-hd message, if applicable delete m_modOnHdHandler; /** * we are about to delete cursors/ranges/... */ emit aboutToDeleteMovingInterfaceContent(this); // kill it early, it has ranges! delete m_onTheFlyChecker; m_onTheFlyChecker = nullptr; clearDictionaryRanges(); // Tell the world that we're about to close (== destruct) // Apps must receive this in a direct signal-slot connection, and prevent // any further use of interfaces once they return. emit aboutToClose(this); // remove file from dirwatch deactivateDirWatch(); // thanks for offering, KPart, but we're already self-destructing setAutoDeleteWidget(false); setAutoDeletePart(false); // clean up remaining views qDeleteAll (m_views.keys()); m_views.clear(); // cu marks for (QHash::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i) { delete i.value(); } m_marks.clear(); delete m_config; KTextEditor::EditorPrivate::self()->deregisterDocument(this); } //END void KTextEditor::DocumentPrivate::saveEditingPositions(KTextEditor::Document *, const KTextEditor::Range &range) { if (m_editingStackPosition != m_editingStack.size() - 1) { m_editingStack.resize(m_editingStackPosition); } KTextEditor::MovingInterface *moving = qobject_cast(this); const auto c = range.start(); QSharedPointer mc (moving->newMovingCursor(c)); if (!m_editingStack.isEmpty() && c.line() == m_editingStack.top()->line()) { m_editingStack.pop(); } m_editingStack.push(mc); if (m_editingStack.size() > s_editingStackSizeLimit) { m_editingStack.removeFirst(); } m_editingStackPosition = m_editingStack.size() - 1; } KTextEditor::Cursor KTextEditor::DocumentPrivate::lastEditingPosition(EditingPositionKind nextOrPrev, KTextEditor::Cursor currentCursor) { if (m_editingStack.isEmpty()) { return KTextEditor::Cursor::invalid(); } auto targetPos = m_editingStack.at(m_editingStackPosition)->toCursor(); if (targetPos == currentCursor) { if (nextOrPrev == Previous) { m_editingStackPosition--; } else { m_editingStackPosition++; } m_editingStackPosition = qBound(0, m_editingStackPosition, m_editingStack.size() - 1); } return m_editingStack.at(m_editingStackPosition)->toCursor(); } void KTextEditor::DocumentPrivate::clearEditingPosStack() { m_editingStack.clear(); m_editingStackPosition = -1; } // on-demand view creation QWidget *KTextEditor::DocumentPrivate::widget() { // no singleViewMode -> no widget()... if (!singleViewMode()) { return nullptr; } // does a widget exist already? use it! if (KTextEditor::Document::widget()) { return KTextEditor::Document::widget(); } // create and return one... KTextEditor::View *view = (KTextEditor::View *)createView(nullptr); insertChildClient(view); view->setContextMenu(view->defaultContextMenu()); setWidget(view); return view; } //BEGIN KTextEditor::Document stuff KTextEditor::View *KTextEditor::DocumentPrivate::createView(QWidget *parent, KTextEditor::MainWindow *mainWindow) { KTextEditor::ViewPrivate *newView = new KTextEditor::ViewPrivate(this, parent, mainWindow); if (m_fileChangedDialogsActivated) { connect(newView, SIGNAL(focusIn(KTextEditor::View*)), this, SLOT(slotModifiedOnDisk())); } emit viewCreated(this, newView); // post existing messages to the new view, if no specific view is given foreach (KTextEditor::Message *message, m_messageHash.keys()) { if (!message->view()) { newView->postMessage(message, m_messageHash[message]); } } return newView; } KTextEditor::Range KTextEditor::DocumentPrivate::rangeOnLine(KTextEditor::Range range, int line) const { const int col1 = toVirtualColumn(range.start()); const int col2 = toVirtualColumn(range.end()); return KTextEditor::Range(line, fromVirtualColumn(line, col1), line, fromVirtualColumn(line, col2)); } //BEGIN KTextEditor::EditInterface stuff bool KTextEditor::DocumentPrivate::isEditingTransactionRunning() const { return editSessionNumber > 0; } QString KTextEditor::DocumentPrivate::text() const { return m_buffer->text(); } QString KTextEditor::DocumentPrivate::text(const KTextEditor::Range &range, bool blockwise) const { if (!range.isValid()) { qCWarning(LOG_KTE) << "Text requested for invalid range" << range; return QString(); } QString s; if (range.start().line() == range.end().line()) { if (range.start().column() > range.end().column()) { return QString(); } Kate::TextLine textLine = m_buffer->plainLine(range.start().line()); if (!textLine) { return QString(); } return textLine->string(range.start().column(), range.end().column() - range.start().column()); } else { for (int i = range.start().line(); (i <= range.end().line()) && (i < m_buffer->count()); ++i) { Kate::TextLine textLine = m_buffer->plainLine(i); if (!blockwise) { if (i == range.start().line()) { s.append(textLine->string(range.start().column(), textLine->length() - range.start().column())); } else if (i == range.end().line()) { s.append(textLine->string(0, range.end().column())); } else { s.append(textLine->string()); } } else { KTextEditor::Range subRange = rangeOnLine(range, i); s.append(textLine->string(subRange.start().column(), subRange.columnWidth())); } if (i < range.end().line()) { s.append(QLatin1Char('\n')); } } } return s; } QChar KTextEditor::DocumentPrivate::characterAt(const KTextEditor::Cursor &position) const { Kate::TextLine textLine = m_buffer->plainLine(position.line()); if (!textLine) { return QChar(); } return textLine->at(position.column()); } QString KTextEditor::DocumentPrivate::wordAt(const KTextEditor::Cursor &cursor) const { return text(wordRangeAt(cursor)); } KTextEditor::Range KTextEditor::DocumentPrivate::wordRangeAt(const KTextEditor::Cursor &cursor) const { // get text line const int line = cursor.line(); Kate::TextLine textLine = m_buffer->plainLine(line); if (!textLine) { return KTextEditor::Range::invalid(); } // make sure the cursor is const int lineLenth = textLine->length(); if (cursor.column() > lineLenth) { return KTextEditor::Range::invalid(); } int start = cursor.column(); int end = start; while (start > 0 && highlight()->isInWord(textLine->at(start - 1), textLine->attribute(start - 1))) { start--; } while (end < lineLenth && highlight()->isInWord(textLine->at(end), textLine->attribute(end))) { end++; } return KTextEditor::Range(line, start, line, end); } bool KTextEditor::DocumentPrivate::isValidTextPosition(const KTextEditor::Cursor& cursor) const { const int ln = cursor.line(); const int col = cursor.column(); // cursor in document range? if (ln < 0 || col < 0 || ln >= lines() || col > lineLength(ln)) { return false; } const QString str = line(ln); Q_ASSERT(str.length() >= col); // cursor at end of line? const int len = lineLength(ln); if (col == 0 || col == len) { return true; } // cursor in the middle of a valid utf32-surrogate? return (! str.at(col).isLowSurrogate()) || (! str.at(col-1).isHighSurrogate()); } QStringList KTextEditor::DocumentPrivate::textLines(const KTextEditor::Range &range, bool blockwise) const { QStringList ret; if (!range.isValid()) { qCWarning(LOG_KTE) << "Text requested for invalid range" << range; return ret; } if (blockwise && (range.start().column() > range.end().column())) { return ret; } if (range.start().line() == range.end().line()) { Q_ASSERT(range.start() <= range.end()); Kate::TextLine textLine = m_buffer->plainLine(range.start().line()); if (!textLine) { return ret; } ret << textLine->string(range.start().column(), range.end().column() - range.start().column()); } else { for (int i = range.start().line(); (i <= range.end().line()) && (i < m_buffer->count()); ++i) { Kate::TextLine textLine = m_buffer->plainLine(i); if (!blockwise) { if (i == range.start().line()) { ret << textLine->string(range.start().column(), textLine->length() - range.start().column()); } else if (i == range.end().line()) { ret << textLine->string(0, range.end().column()); } else { ret << textLine->string(); } } else { KTextEditor::Range subRange = rangeOnLine(range, i); ret << textLine->string(subRange.start().column(), subRange.columnWidth()); } } } return ret; } QString KTextEditor::DocumentPrivate::line(int line) const { Kate::TextLine l = m_buffer->plainLine(line); if (!l) { return QString(); } return l->string(); } bool KTextEditor::DocumentPrivate::setText(const QString &s) { if (!isReadWrite()) { return false; } QList msave; foreach (KTextEditor::Mark *mark, m_marks) { msave.append(*mark); } editStart(); // delete the text clear(); // insert the new text insertText(KTextEditor::Cursor(), s); editEnd(); foreach (KTextEditor::Mark mark, msave) { setMark(mark.line, mark.type); } return true; } bool KTextEditor::DocumentPrivate::setText(const QStringList &text) { if (!isReadWrite()) { return false; } QList msave; foreach (KTextEditor::Mark *mark, m_marks) { msave.append(*mark); } editStart(); // delete the text clear(); // insert the new text insertText(KTextEditor::Cursor::start(), text); editEnd(); foreach (KTextEditor::Mark mark, msave) { setMark(mark.line, mark.type); } return true; } bool KTextEditor::DocumentPrivate::clear() { if (!isReadWrite()) { return false; } foreach (KTextEditor::ViewPrivate *view, m_views) { view->clear(); view->tagAll(); view->update(); } clearMarks(); emit aboutToInvalidateMovingInterfaceContent(this); m_buffer->invalidateRanges(); emit aboutToRemoveText(documentRange()); return editRemoveLines(0, lastLine()); } bool KTextEditor::DocumentPrivate::insertText(const KTextEditor::Cursor &position, const QString &text, bool block) { if (!isReadWrite()) { return false; } if (text.isEmpty()) { return true; } editStart(); int currentLine = position.line(); int currentLineStart = 0; const int totalLength = text.length(); int insertColumn = position.column(); // pad with empty lines, if insert position is after last line if (position.line() > lines()) { int line = lines(); while (line <= position.line()) { editInsertLine(line, QString()); line++; } } const int tabWidth = config()->tabWidth(); static const QChar newLineChar(QLatin1Char('\n')); int insertColumnExpanded = insertColumn; Kate::TextLine l = plainKateTextLine(currentLine); if (l) { insertColumnExpanded = l->toVirtualColumn(insertColumn, tabWidth); } int positionColumnExpanded = insertColumnExpanded; int pos = 0; for (; pos < totalLength; pos++) { const QChar &ch = text.at(pos); if (ch == newLineChar) { // Only perform the text insert if there is text to insert if (currentLineStart < pos) { editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart)); } if (!block) { editWrapLine(currentLine, insertColumn + pos - currentLineStart); insertColumn = 0; } currentLine++; l = plainKateTextLine(currentLine); if (block) { if (currentLine == lastLine() + 1) { editInsertLine(currentLine, QString()); } insertColumn = positionColumnExpanded; if (l) { insertColumn = l->fromVirtualColumn(insertColumn, tabWidth); } } currentLineStart = pos + 1; if (l) { insertColumnExpanded = l->toVirtualColumn(insertColumn, tabWidth); } } } // Only perform the text insert if there is text to insert if (currentLineStart < pos) { editInsertText(currentLine, insertColumn, text.mid(currentLineStart, pos - currentLineStart)); } editEnd(); return true; } bool KTextEditor::DocumentPrivate::insertText(const KTextEditor::Cursor &position, const QStringList &textLines, bool block) { if (!isReadWrite()) { return false; } // just reuse normal function return insertText(position, textLines.join(QStringLiteral("\n")), block); } bool KTextEditor::DocumentPrivate::removeText(const KTextEditor::Range &_range, bool block) { KTextEditor::Range range = _range; if (!isReadWrite()) { return false; } // Should now be impossible to trigger with the new Range class Q_ASSERT(range.start().line() <= range.end().line()); if (range.start().line() > lastLine()) { return false; } if (!block) { emit aboutToRemoveText(range); } editStart(); if (!block) { if (range.end().line() > lastLine()) { range.setEnd(KTextEditor::Cursor(lastLine() + 1, 0)); } if (range.onSingleLine()) { editRemoveText(range.start().line(), range.start().column(), range.columnWidth()); } else { int from = range.start().line(); int to = range.end().line(); // remove last line if (to <= lastLine()) { editRemoveText(to, 0, range.end().column()); } // editRemoveLines() will be called on first line (to remove bookmark) if (range.start().column() == 0 && from > 0) { --from; } // remove middle lines editRemoveLines(from + 1, to - 1); // remove first line if not already removed by editRemoveLines() if (range.start().column() > 0 || range.start().line() == 0) { editRemoveText(from, range.start().column(), m_buffer->plainLine(from)->length() - range.start().column()); editUnWrapLine(from); } } } // if ( ! block ) else { int startLine = qMax(0, range.start().line()); int vc1 = toVirtualColumn(range.start()); int vc2 = toVirtualColumn(range.end()); for (int line = qMin(range.end().line(), lastLine()); line >= startLine; --line) { int col1 = fromVirtualColumn(line, vc1); int col2 = fromVirtualColumn(line, vc2); editRemoveText(line, qMin(col1, col2), qAbs(col2 - col1)); } } editEnd(); return true; } bool KTextEditor::DocumentPrivate::insertLine(int l, const QString &str) { if (!isReadWrite()) { return false; } if (l < 0 || l > lines()) { return false; } return editInsertLine(l, str); } bool KTextEditor::DocumentPrivate::insertLines(int line, const QStringList &text) { if (!isReadWrite()) { return false; } if (line < 0 || line > lines()) { return false; } bool success = true; foreach (const QString &string, text) { success &= editInsertLine(line++, string); } return success; } bool KTextEditor::DocumentPrivate::removeLine(int line) { if (!isReadWrite()) { return false; } if (line < 0 || line > lastLine()) { return false; } return editRemoveLine(line); } int KTextEditor::DocumentPrivate::totalCharacters() const { int l = 0; for (int i = 0; i < m_buffer->count(); ++i) { Kate::TextLine line = m_buffer->plainLine(i); if (line) { l += line->length(); } } return l; } int KTextEditor::DocumentPrivate::lines() const { return m_buffer->count(); } int KTextEditor::DocumentPrivate::lineLength(int line) const { if (line < 0 || line > lastLine()) { return -1; } Kate::TextLine l = m_buffer->plainLine(line); if (!l) { return -1; } return l->length(); } bool KTextEditor::DocumentPrivate::isLineModified(int line) const { if (line < 0 || line >= lines()) { return false; } Kate::TextLine l = m_buffer->plainLine(line); Q_ASSERT(l); return l->markedAsModified(); } bool KTextEditor::DocumentPrivate::isLineSaved(int line) const { if (line < 0 || line >= lines()) { return false; } Kate::TextLine l = m_buffer->plainLine(line); Q_ASSERT(l); return l->markedAsSavedOnDisk(); } bool KTextEditor::DocumentPrivate::isLineTouched(int line) const { if (line < 0 || line >= lines()) { return false; } Kate::TextLine l = m_buffer->plainLine(line); Q_ASSERT(l); return l->markedAsModified() || l->markedAsSavedOnDisk(); } //END //BEGIN KTextEditor::EditInterface internal stuff // // Starts an edit session with (or without) undo, update of view disabled during session // bool KTextEditor::DocumentPrivate::editStart() { editSessionNumber++; if (editSessionNumber > 1) { return false; } editIsRunning = true; m_undoManager->editStart(); foreach (KTextEditor::ViewPrivate *view, m_views) { view->editStart(); } m_buffer->editStart(); return true; } // // End edit session and update Views // bool KTextEditor::DocumentPrivate::editEnd() { if (editSessionNumber == 0) { Q_ASSERT(0); return false; } // wrap the new/changed text, if something really changed! if (m_buffer->editChanged() && (editSessionNumber == 1)) if (m_undoManager->isActive() && config()->wordWrap()) { wrapText(m_buffer->editTagStart(), m_buffer->editTagEnd()); } editSessionNumber--; if (editSessionNumber > 0) { return false; } // end buffer edit, will trigger hl update // this will cause some possible adjustment of tagline start/end m_buffer->editEnd(); m_undoManager->editEnd(); // edit end for all views !!!!!!!!! foreach (KTextEditor::ViewPrivate *view, m_views) { view->editEnd(m_buffer->editTagStart(), m_buffer->editTagEnd(), m_buffer->editTagFrom()); } if (m_buffer->editChanged()) { setModified(true); emit textChanged(this); } editIsRunning = false; return true; } void KTextEditor::DocumentPrivate::pushEditState() { editStateStack.push(editSessionNumber); } void KTextEditor::DocumentPrivate::popEditState() { if (editStateStack.isEmpty()) { return; } int count = editStateStack.pop() - editSessionNumber; while (count < 0) { ++count; editEnd(); } while (count > 0) { --count; editStart(); } } void KTextEditor::DocumentPrivate::inputMethodStart() { m_undoManager->inputMethodStart(); } void KTextEditor::DocumentPrivate::inputMethodEnd() { m_undoManager->inputMethodEnd(); } bool KTextEditor::DocumentPrivate::wrapText(int startLine, int endLine) { if (startLine < 0 || endLine < 0) { return false; } if (!isReadWrite()) { return false; } int col = config()->wordWrapAt(); if (col == 0) { return false; } editStart(); for (int line = startLine; (line <= endLine) && (line < lines()); line++) { Kate::TextLine l = kateTextLine(line); if (!l) { break; } //qCDebug(LOG_KTE) << "try wrap line: " << line; if (l->virtualLength(m_buffer->tabWidth()) > col) { Kate::TextLine nextl = kateTextLine(line + 1); //qCDebug(LOG_KTE) << "do wrap line: " << line; int eolPosition = l->length() - 1; // take tabs into account here, too int x = 0; const QString &t = l->string(); int z2 = 0; for (; z2 < l->length(); z2++) { static const QChar tabChar(QLatin1Char('\t')); if (t.at(z2) == tabChar) { x += m_buffer->tabWidth() - (x % m_buffer->tabWidth()); } else { x++; } if (x > col) { break; } } const int colInChars = qMin(z2, l->length() - 1); int searchStart = colInChars; // If where we are wrapping is an end of line and is a space we don't // want to wrap there if (searchStart == eolPosition && t.at(searchStart).isSpace()) { searchStart--; } // Scan backwards looking for a place to break the line // We are not interested in breaking at the first char // of the line (if it is a space), but we are at the second // anders: if we can't find a space, try breaking on a word // boundary, using KateHighlight::canBreakAt(). // This could be a priority (setting) in the hl/filetype/document int z = -1; int nw = -1; // alternative position, a non word character for (z = searchStart; z >= 0; z--) { if (t.at(z).isSpace()) { break; } if ((nw < 0) && highlight()->canBreakAt(t.at(z), l->attribute(z))) { nw = z; } } if (z >= 0) { // So why don't we just remove the trailing space right away? // Well, the (view's) cursor may be directly in front of that space // (user typing text before the last word on the line), and if that // happens, the cursor would be moved to the next line, which is not // what we want (bug #106261) z++; } else { // There was no space to break at so break at a nonword character if // found, or at the wrapcolumn ( that needs be configurable ) // Don't try and add any white space for the break if ((nw >= 0) && nw < colInChars) { nw++; // break on the right side of the character } z = (nw >= 0) ? nw : colInChars; } if (nextl && !nextl->isAutoWrapped()) { editWrapLine(line, z, true); editMarkLineAutoWrapped(line + 1, true); endLine++; } else { if (nextl && (nextl->length() > 0) && !nextl->at(0).isSpace() && ((l->length() < 1) || !l->at(l->length() - 1).isSpace())) { editInsertText(line + 1, 0, QLatin1String(" ")); } bool newLineAdded = false; editWrapLine(line, z, false, &newLineAdded); editMarkLineAutoWrapped(line + 1, true); endLine++; } } } editEnd(); return true; } bool KTextEditor::DocumentPrivate::editInsertText(int line, int col, const QString &s) { // verbose debug EDIT_DEBUG << "editInsertText" << line << col << s; if (line < 0 || col < 0) { return false; } if (!isReadWrite()) { return false; } Kate::TextLine l = kateTextLine(line); if (!l) { return false; } // nothing to do, do nothing! if (s.isEmpty()) { return true; } editStart(); QString s2 = s; int col2 = col; if (col2 > l->length()) { s2 = QString(col2 - l->length(), QLatin1Char(' ')) + s; col2 = l->length(); } m_undoManager->slotTextInserted(line, col2, s2); // insert text into line m_buffer->insertText(KTextEditor::Cursor(line, col2), s2); emit textInserted(this, KTextEditor::Range(line, col2, line, col2 + s2.length())); editEnd(); return true; } bool KTextEditor::DocumentPrivate::editRemoveText(int line, int col, int len) { // verbose debug EDIT_DEBUG << "editRemoveText" << line << col << len; if (line < 0 || col < 0 || len < 0) { return false; } if (!isReadWrite()) { return false; } Kate::TextLine l = kateTextLine(line); if (!l) { return false; } // nothing to do, do nothing! if (len == 0) { return true; } // wrong column if (col >= l->text().size()) { return false; } // don't try to remove what's not there len = qMin(len, l->text().size() - col); editStart(); QString oldText = l->string().mid(col, len); m_undoManager->slotTextRemoved(line, col, oldText); // remove text from line m_buffer->removeText(KTextEditor::Range(KTextEditor::Cursor(line, col), KTextEditor::Cursor(line, col + len))); emit textRemoved(this, KTextEditor::Range(line, col, line, col + len), oldText); editEnd(); return true; } bool KTextEditor::DocumentPrivate::editMarkLineAutoWrapped(int line, bool autowrapped) { // verbose debug EDIT_DEBUG << "editMarkLineAutoWrapped" << line << autowrapped; if (line < 0) { return false; } if (!isReadWrite()) { return false; } Kate::TextLine l = kateTextLine(line); if (!l) { return false; } editStart(); m_undoManager->slotMarkLineAutoWrapped(line, autowrapped); l->setAutoWrapped(autowrapped); editEnd(); return true; } bool KTextEditor::DocumentPrivate::editWrapLine(int line, int col, bool newLine, bool *newLineAdded) { // verbose debug EDIT_DEBUG << "editWrapLine" << line << col << newLine; if (line < 0 || col < 0) { return false; } if (!isReadWrite()) { return false; } Kate::TextLine l = kateTextLine(line); if (!l) { return false; } editStart(); Kate::TextLine nextLine = kateTextLine(line + 1); const int length = l->length(); m_undoManager->slotLineWrapped(line, col, length - col, (!nextLine || newLine)); if (!nextLine || newLine) { m_buffer->wrapLine(KTextEditor::Cursor(line, col)); QList list; for (QHash::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i) { if (i.value()->line >= line) { if ((col == 0) || (i.value()->line > line)) { list.append(i.value()); } } } for (int i = 0; i < list.size(); ++i) { m_marks.take(list.at(i)->line); } for (int i = 0; i < list.size(); ++i) { list.at(i)->line++; m_marks.insert(list.at(i)->line, list.at(i)); } if (!list.isEmpty()) { emit marksChanged(this); } // yes, we added a new line ! if (newLineAdded) { (*newLineAdded) = true; } } else { m_buffer->wrapLine(KTextEditor::Cursor(line, col)); m_buffer->unwrapLine(line + 2); // no, no new line added ! if (newLineAdded) { (*newLineAdded) = false; } } emit textInserted(this, KTextEditor::Range(line, col, line + 1, 0)); editEnd(); return true; } bool KTextEditor::DocumentPrivate::editUnWrapLine(int line, bool removeLine, int length) { // verbose debug EDIT_DEBUG << "editUnWrapLine" << line << removeLine << length; if (line < 0 || length < 0) { return false; } if (!isReadWrite()) { return false; } Kate::TextLine l = kateTextLine(line); Kate::TextLine nextLine = kateTextLine(line + 1); if (!l || !nextLine) { return false; } editStart(); int col = l->length(); m_undoManager->slotLineUnWrapped(line, col, length, removeLine); if (removeLine) { m_buffer->unwrapLine(line + 1); } else { m_buffer->wrapLine(KTextEditor::Cursor(line + 1, length)); m_buffer->unwrapLine(line + 1); } QList list; for (QHash::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i) { if (i.value()->line >= line + 1) { list.append(i.value()); } if (i.value()->line == line + 1) { KTextEditor::Mark *mark = m_marks.take(line); if (mark) { i.value()->type |= mark->type; } } } for (int i = 0; i < list.size(); ++i) { m_marks.take(list.at(i)->line); } for (int i = 0; i < list.size(); ++i) { list.at(i)->line--; m_marks.insert(list.at(i)->line, list.at(i)); } if (!list.isEmpty()) { emit marksChanged(this); } emit textRemoved(this, KTextEditor::Range(line, col, line + 1, 0), QStringLiteral("\n")); editEnd(); return true; } bool KTextEditor::DocumentPrivate::editInsertLine(int line, const QString &s) { // verbose debug EDIT_DEBUG << "editInsertLine" << line << s; if (line < 0) { return false; } if (!isReadWrite()) { return false; } if (line > lines()) { return false; } editStart(); m_undoManager->slotLineInserted(line, s); // wrap line if (line > 0) { Kate::TextLine previousLine = m_buffer->line(line - 1); m_buffer->wrapLine(KTextEditor::Cursor(line - 1, previousLine->text().size())); } else { m_buffer->wrapLine(KTextEditor::Cursor(0, 0)); } // insert text m_buffer->insertText(KTextEditor::Cursor(line, 0), s); Kate::TextLine tl = m_buffer->line(line); QList list; for (QHash::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i) { if (i.value()->line >= line) { list.append(i.value()); } } for (int i = 0; i < list.size(); ++i) { m_marks.take(list.at(i)->line); } for (int i = 0; i < list.size(); ++i) { list.at(i)->line++; m_marks.insert(list.at(i)->line, list.at(i)); } if (!list.isEmpty()) { emit marksChanged(this); } KTextEditor::Range rangeInserted(line, 0, line, tl->length()); if (line) { Kate::TextLine prevLine = plainKateTextLine(line - 1); rangeInserted.setStart(KTextEditor::Cursor(line - 1, prevLine->length())); } else { rangeInserted.setEnd(KTextEditor::Cursor(line + 1, 0)); } emit textInserted(this, rangeInserted); editEnd(); return true; } bool KTextEditor::DocumentPrivate::editRemoveLine(int line) { return editRemoveLines(line, line); } bool KTextEditor::DocumentPrivate::editRemoveLines(int from, int to) { // verbose debug EDIT_DEBUG << "editRemoveLines" << from << to; if (to < from || from < 0 || to > lastLine()) { return false; } if (!isReadWrite()) { return false; } if (lines() == 1) { return editRemoveText(0, 0, kateTextLine(0)->length()); } editStart(); QStringList oldText; /** * first remove text */ for (int line = to; line >= from; --line) { Kate::TextLine tl = m_buffer->line(line); oldText.prepend(this->line(line)); m_undoManager->slotLineRemoved(line, this->line(line)); m_buffer->removeText(KTextEditor::Range(KTextEditor::Cursor(line, 0), KTextEditor::Cursor(line, tl->text().size()))); } /** * then collapse lines */ for (int line = to; line >= from; --line) { /** * unwrap all lines, prefer to unwrap line behind, skip to wrap line 0 */ if (line + 1 < m_buffer->lines()) { m_buffer->unwrapLine(line + 1); } else if (line) { m_buffer->unwrapLine(line); } } QList rmark; QList list; foreach (KTextEditor::Mark *mark, m_marks) { int line = mark->line; if (line > to) { list << line; } else if (line >= from) { rmark << line; } } foreach (int line, rmark) { delete m_marks.take(line); } foreach (int line, list) { KTextEditor::Mark *mark = m_marks.take(line); mark->line -= to - from + 1; m_marks.insert(mark->line, mark); } if (!list.isEmpty()) { emit marksChanged(this); } KTextEditor::Range rangeRemoved(from, 0, to + 1, 0); if (to == lastLine() + to - from + 1) { rangeRemoved.setEnd(KTextEditor::Cursor(to, oldText.last().length())); if (from > 0) { Kate::TextLine prevLine = plainKateTextLine(from - 1); rangeRemoved.setStart(KTextEditor::Cursor(from - 1, prevLine->length())); } } emit textRemoved(this, rangeRemoved, oldText.join(QStringLiteral("\n")) + QLatin1Char('\n')); editEnd(); return true; } //END //BEGIN KTextEditor::UndoInterface stuff uint KTextEditor::DocumentPrivate::undoCount() const { return m_undoManager->undoCount(); } uint KTextEditor::DocumentPrivate::redoCount() const { return m_undoManager->redoCount(); } void KTextEditor::DocumentPrivate::undo() { m_undoManager->undo(); } void KTextEditor::DocumentPrivate::redo() { m_undoManager->redo(); } //END //BEGIN KTextEditor::SearchInterface stuff QVector KTextEditor::DocumentPrivate::searchText( const KTextEditor::Range &range, const QString &pattern, const KTextEditor::SearchOptions options) const { const bool escapeSequences = options.testFlag(KTextEditor::EscapeSequences); const bool regexMode = options.testFlag(KTextEditor::Regex); const bool backwards = options.testFlag(KTextEditor::Backwards); const bool wholeWords = options.testFlag(KTextEditor::WholeWords); const Qt::CaseSensitivity caseSensitivity = options.testFlag(KTextEditor::CaseInsensitive) ? Qt::CaseInsensitive : Qt::CaseSensitive; if (regexMode) { // regexp search // escape sequences are supported by definition KateRegExpSearch searcher(this, caseSensitivity); return searcher.search(pattern, range, backwards); } if (escapeSequences) { // escaped search KatePlainTextSearch searcher(this, caseSensitivity, wholeWords); KTextEditor::Range match = searcher.search(KateRegExpSearch::escapePlaintext(pattern), range, backwards); QVector result; result.append(match); return result; } // plaintext search KatePlainTextSearch searcher(this, caseSensitivity, wholeWords); KTextEditor::Range match = searcher.search(pattern, range, backwards); QVector result; result.append(match); return result; } //END QWidget *KTextEditor::DocumentPrivate::dialogParent() { QWidget *w = widget(); if (!w) { w = activeView(); if (!w) { w = QApplication::activeWindow(); } } return w; } //BEGIN KTextEditor::HighlightingInterface stuff bool KTextEditor::DocumentPrivate::setMode(const QString &name) { updateFileType(name); return true; } KTextEditor::DefaultStyle KTextEditor::DocumentPrivate::defaultStyleAt(const KTextEditor::Cursor &position) const { // TODO, FIXME KDE5: in surrogate, use 2 bytes before if (! isValidTextPosition(position)) { return dsNormal; } int ds = const_cast(this)-> defStyleNum(position.line(), position.column()); if (ds < 0 || ds > static_cast(dsError)) { return dsNormal; } return static_cast(ds); } QString KTextEditor::DocumentPrivate::mode() const { return m_fileType; } QStringList KTextEditor::DocumentPrivate::modes() const { QStringList m; const QList &modeList = KTextEditor::EditorPrivate::self()->modeManager()->list(); foreach (KateFileType *type, modeList) { m << type->name; } return m; } bool KTextEditor::DocumentPrivate::setHighlightingMode(const QString &name) { int mode = KateHlManager::self()->nameFind(name); if (mode == -1) { return false; } m_buffer->setHighlight(mode); return true; } QString KTextEditor::DocumentPrivate::highlightingMode() const { return highlight()->name(); } QStringList KTextEditor::DocumentPrivate::highlightingModes() const { QStringList hls; for (int i = 0; i < KateHlManager::self()->highlights(); ++i) { hls << KateHlManager::self()->hlName(i); } return hls; } QString KTextEditor::DocumentPrivate::highlightingModeSection(int index) const { return KateHlManager::self()->hlSection(index); } QString KTextEditor::DocumentPrivate::modeSection(int index) const { return KTextEditor::EditorPrivate::self()->modeManager()->list().at(index)->section; } void KTextEditor::DocumentPrivate::bufferHlChanged() { // update all views makeAttribs(false); // deactivate indenter if necessary m_indenter->checkRequiredStyle(); emit highlightingModeChanged(this); } void KTextEditor::DocumentPrivate::setDontChangeHlOnSave() { m_hlSetByUser = true; } void KTextEditor::DocumentPrivate::bomSetByUser() { m_bomSetByUser = true; } //END //BEGIN KTextEditor::SessionConfigInterface and KTextEditor::ParameterizedSessionConfigInterface stuff void KTextEditor::DocumentPrivate::readSessionConfig(const KConfigGroup &kconfig, const QSet &flags) { if (!flags.contains(QStringLiteral("SkipEncoding"))) { // get the encoding QString tmpenc = kconfig.readEntry("Encoding"); if (!tmpenc.isEmpty() && (tmpenc != encoding())) { setEncoding(tmpenc); } } if (!flags.contains(QStringLiteral("SkipUrl"))) { // restore the url QUrl url(kconfig.readEntry("URL")); // open the file if url valid if (!url.isEmpty() && url.isValid()) { openUrl(url); } else { completed(); //perhaps this should be emitted at the end of this function } } else { completed(); //perhaps this should be emitted at the end of this function } if (!flags.contains(QStringLiteral("SkipMode"))) { // restore the filetype if (kconfig.hasKey("Mode")) { updateFileType(kconfig.readEntry("Mode", fileType())); // restore if set by user, too! m_fileTypeSetByUser = kconfig.readEntry("Mode Set By User", false); } } if (!flags.contains(QStringLiteral("SkipHighlighting"))) { // restore the hl stuff if (kconfig.hasKey("Highlighting")) { const int mode = KateHlManager::self()->nameFind(kconfig.readEntry("Highlighting")); if (mode >= 0) { m_buffer->setHighlight(mode); // restore if set by user, too! see bug 332605, otherwise we loose the hl later again on save m_hlSetByUser = kconfig.readEntry("Highlighting Set By User", false); } } } // indent mode config()->setIndentationMode(kconfig.readEntry("Indentation Mode", config()->indentationMode())); // Restore Bookmarks const QList marks = kconfig.readEntry("Bookmarks", QList()); for (int i = 0; i < marks.count(); i++) { addMark(marks.at(i), KTextEditor::DocumentPrivate::markType01); } } void KTextEditor::DocumentPrivate::writeSessionConfig(KConfigGroup &kconfig, const QSet &flags) { if (this->url().isLocalFile()) { const QString path = this->url().toLocalFile(); if (path.startsWith(QDir::tempPath())) { return; // inside tmp resource, do not save } } if (!flags.contains(QStringLiteral("SkipUrl"))) { // save url kconfig.writeEntry("URL", this->url().toString()); } if (!flags.contains(QStringLiteral("SkipEncoding"))) { // save encoding kconfig.writeEntry("Encoding", encoding()); } if (!flags.contains(QStringLiteral("SkipMode"))) { // save file type kconfig.writeEntry("Mode", m_fileType); // save if set by user, too! kconfig.writeEntry("Mode Set By User", m_fileTypeSetByUser); } if (!flags.contains(QStringLiteral("SkipHighlighting"))) { // save hl kconfig.writeEntry("Highlighting", highlight()->name()); // save if set by user, too! see bug 332605, otherwise we loose the hl later again on save kconfig.writeEntry("Highlighting Set By User", m_hlSetByUser); } // indent mode kconfig.writeEntry("Indentation Mode", config()->indentationMode()); // Save Bookmarks QList marks; for (QHash::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i) if (i.value()->type & KTextEditor::MarkInterface::markType01) { marks << i.value()->line; } kconfig.writeEntry("Bookmarks", marks); } //END KTextEditor::SessionConfigInterface and KTextEditor::ParameterizedSessionConfigInterface stuff uint KTextEditor::DocumentPrivate::mark(int line) { KTextEditor::Mark *m = m_marks.value(line); if (!m) { return 0; } return m->type; } void KTextEditor::DocumentPrivate::setMark(int line, uint markType) { clearMark(line); addMark(line, markType); } void KTextEditor::DocumentPrivate::clearMark(int line) { if (line < 0 || line > lastLine()) { return; } if (!m_marks.value(line)) { return; } KTextEditor::Mark *mark = m_marks.take(line); emit markChanged(this, *mark, MarkRemoved); emit marksChanged(this); delete mark; tagLines(line, line); repaintViews(true); } void KTextEditor::DocumentPrivate::addMark(int line, uint markType) { KTextEditor::Mark *mark; if (line < 0 || line > lastLine()) { return; } if (markType == 0) { return; } if ((mark = m_marks.value(line))) { // Remove bits already set markType &= ~mark->type; if (markType == 0) { return; } // Add bits mark->type |= markType; } else { mark = new KTextEditor::Mark; mark->line = line; mark->type = markType; m_marks.insert(line, mark); } // Emit with a mark having only the types added. KTextEditor::Mark temp; temp.line = line; temp.type = markType; emit markChanged(this, temp, MarkAdded); emit marksChanged(this); tagLines(line, line); repaintViews(true); } void KTextEditor::DocumentPrivate::removeMark(int line, uint markType) { if (line < 0 || line > lastLine()) { return; } KTextEditor::Mark *mark = m_marks.value(line); if (!mark) { return; } // Remove bits not set markType &= mark->type; if (markType == 0) { return; } // Subtract bits mark->type &= ~markType; // Emit with a mark having only the types removed. KTextEditor::Mark temp; temp.line = line; temp.type = markType; emit markChanged(this, temp, MarkRemoved); if (mark->type == 0) { m_marks.remove(line); delete mark; } emit marksChanged(this); tagLines(line, line); repaintViews(true); } const QHash &KTextEditor::DocumentPrivate::marks() { return m_marks; } void KTextEditor::DocumentPrivate::requestMarkTooltip(int line, QPoint position) { KTextEditor::Mark *mark = m_marks.value(line); if (!mark) { return; } bool handled = false; emit markToolTipRequested(this, *mark, position, handled); } bool KTextEditor::DocumentPrivate::handleMarkClick(int line) { bool handled = false; KTextEditor::Mark *mark = m_marks.value(line); if (!mark) { emit markClicked(this, KTextEditor::Mark{line, 0}, handled); } else { emit markClicked(this, *mark, handled); } return handled; } bool KTextEditor::DocumentPrivate::handleMarkContextMenu(int line, QPoint position) { bool handled = false; KTextEditor::Mark *mark = m_marks.value(line); if (!mark) { emit markContextMenuRequested(this, KTextEditor::Mark{line, 0}, position, handled); } else { emit markContextMenuRequested(this, *mark, position, handled); } return handled; } void KTextEditor::DocumentPrivate::clearMarks() { while (!m_marks.isEmpty()) { QHash::iterator it = m_marks.begin(); KTextEditor::Mark mark = *it.value(); delete it.value(); m_marks.erase(it); emit markChanged(this, mark, MarkRemoved); tagLines(mark.line, mark.line); } m_marks.clear(); emit marksChanged(this); repaintViews(true); } void KTextEditor::DocumentPrivate::setMarkPixmap(MarkInterface::MarkTypes type, const QPixmap &pixmap) { m_markPixmaps.insert(type, pixmap); } void KTextEditor::DocumentPrivate::setMarkDescription(MarkInterface::MarkTypes type, const QString &description) { m_markDescriptions.insert(type, description); } QPixmap KTextEditor::DocumentPrivate::markPixmap(MarkInterface::MarkTypes type) const { return m_markPixmaps.value(type, QPixmap()); } QColor KTextEditor::DocumentPrivate::markColor(MarkInterface::MarkTypes type) const { uint reserved = (0x1 << KTextEditor::MarkInterface::reservedMarkersCount()) - 1; if ((uint)type >= (uint)markType01 && (uint)type <= reserved) { return KateRendererConfig::global()->lineMarkerColor(type); } else { return QColor(); } } QString KTextEditor::DocumentPrivate::markDescription(MarkInterface::MarkTypes type) const { return m_markDescriptions.value(type, QString()); } void KTextEditor::DocumentPrivate::setEditableMarks(uint markMask) { m_editableMarks = markMask; } uint KTextEditor::DocumentPrivate::editableMarks() const { return m_editableMarks; } //END //BEGIN KTextEditor::PrintInterface stuff bool KTextEditor::DocumentPrivate::print() { return KatePrinter::print(this); } void KTextEditor::DocumentPrivate::printPreview() { KatePrinter::printPreview(this); } //END KTextEditor::PrintInterface stuff //BEGIN KTextEditor::DocumentInfoInterface (### unfinished) QString KTextEditor::DocumentPrivate::mimeType() { /** * collect first 4k of text * only heuristic */ QByteArray buf; for (int i = 0; (i < lines()) && (buf.size() <= 4096); ++i) { buf.append(line(i).toUtf8()); buf.append('\n'); } // use path of url, too, if set if (!url().path().isEmpty()) { return QMimeDatabase().mimeTypeForFileNameAndData(url().path(), buf).name(); } // else only use the content return QMimeDatabase().mimeTypeForData(buf).name(); } //END KTextEditor::DocumentInfoInterface //BEGIN: error void KTextEditor::DocumentPrivate::showAndSetOpeningErrorAccess() { QPointer message = new KTextEditor::Message(i18n("The file %1 could not be loaded, as it was not possible to read from it.
Check if you have read access to this file.", this->url().toDisplayString(QUrl::PreferLocalFile)), KTextEditor::Message::Error); message->setWordWrap(true); QAction *tryAgainAction = new QAction(QIcon::fromTheme(QStringLiteral("view-refresh")), i18nc("translators: you can also translate 'Try Again' with 'Reload'", "Try Again"), nullptr); connect(tryAgainAction, SIGNAL(triggered()), SLOT(documentReload()), Qt::QueuedConnection); QAction *closeAction = new QAction(QIcon::fromTheme(QStringLiteral("window-close")), i18n("&Close"), nullptr); closeAction->setToolTip(i18n("Close message")); // add try again and close actions message->addAction(tryAgainAction); message->addAction(closeAction); // finally post message postMessage(message); // remember error m_openingError = true; m_openingErrorMessage = i18n("The file %1 could not be loaded, as it was not possible to read from it.\n\nCheck if you have read access to this file.", this->url().toDisplayString(QUrl::PreferLocalFile)); } //END: error #ifdef Q_OS_ANDROID int log2(double d) { int result; std::frexp(d, &result); return result-1; } #endif void KTextEditor::DocumentPrivate::openWithLineLengthLimitOverride() { // raise line length limit to the next power of 2 const int longestLine = m_buffer->longestLineLoaded(); int newLimit = pow(2, ceil(log2(longestLine))); if (newLimit <= longestLine) { newLimit *= 2; } // do the raise config()->setLineLengthLimit(newLimit); // just reload m_buffer->clear(); openFile(); if (!m_openingError) { setReadWrite(true); m_readWriteStateBeforeLoading = true; } } int KTextEditor::DocumentPrivate::lineLengthLimit() const { return config()->lineLengthLimit(); } //BEGIN KParts::ReadWrite stuff bool KTextEditor::DocumentPrivate::openFile() { /** * we are about to invalidate all cursors/ranges/.. => m_buffer->openFile will do so */ emit aboutToInvalidateMovingInterfaceContent(this); // no open errors until now... m_openingError = false; m_openingErrorMessage.clear (); // add new m_file to dirwatch activateDirWatch(); // remember current encoding QString currentEncoding = encoding(); // // mime type magic to get encoding right // QString mimeType = arguments().mimeType(); int pos = mimeType.indexOf(QLatin1Char(';')); if (pos != -1 && !(m_reloading && m_userSetEncodingForNextReload)) { setEncoding(mimeType.mid(pos + 1)); } // update file type, we do this here PRE-LOAD, therefore pass file name for reading from updateFileType(KTextEditor::EditorPrivate::self()->modeManager()->fileType(this, localFilePath())); // read dir config (if possible and wanted) // do this PRE-LOAD to get encoding info! readDirConfig(); // perhaps we need to re-set again the user encoding if (m_reloading && m_userSetEncodingForNextReload && (currentEncoding != encoding())) { setEncoding(currentEncoding); } bool success = m_buffer->openFile(localFilePath(), (m_reloading && m_userSetEncodingForNextReload)); // // yeah, success // read variables // if (success) { readVariables(); } // // update views // foreach (KTextEditor::ViewPrivate *view, m_views) { // This is needed here because inserting the text moves the view's start position (it is a MovingCursor) view->setCursorPosition(KTextEditor::Cursor()); view->updateView(true); } // Inform that the text has changed (required as we're not inside the usual editStart/End stuff) emit textChanged(this); emit loaded(this); // // to houston, we are not modified // if (m_modOnHd) { m_modOnHd = false; m_modOnHdReason = OnDiskUnmodified; m_prevModOnHdReason = OnDiskUnmodified; emit modifiedOnDisk(this, m_modOnHd, m_modOnHdReason); } // // display errors // if (!success) { showAndSetOpeningErrorAccess(); } // warn: broken encoding if (m_buffer->brokenEncoding()) { // this file can't be saved again without killing it setReadWrite(false); m_readWriteStateBeforeLoading=false; QPointer message = new KTextEditor::Message(i18n("The file %1 was opened with %2 encoding but contained invalid characters.
" "It is set to read-only mode, as saving might destroy its content.
" "Either reopen the file with the correct encoding chosen or enable the read-write mode again in the tools menu to be able to edit it.", this->url().toDisplayString(QUrl::PreferLocalFile), QString::fromLatin1(m_buffer->textCodec()->name())), KTextEditor::Message::Warning); message->setWordWrap(true); postMessage(message); // remember error m_openingError = true; m_openingErrorMessage = i18n("The file %1 was opened with %2 encoding but contained invalid characters." " It is set to read-only mode, as saving might destroy its content." " Either reopen the file with the correct encoding chosen or enable the read-write mode again in the tools menu to be able to edit it.", this->url().toDisplayString(QUrl::PreferLocalFile), QString::fromLatin1(m_buffer->textCodec()->name())); } // warn: too long lines if (m_buffer->tooLongLinesWrapped()) { // this file can't be saved again without modifications setReadWrite(false); m_readWriteStateBeforeLoading = false; QPointer message = new KTextEditor::Message(i18n("The file %1 was opened and contained lines longer than the configured Line Length Limit (%2 characters).
" "The longest of those lines was %3 characters long
" "Those lines were wrapped and the document is set to read-only mode, as saving will modify its content.", this->url().toDisplayString(QUrl::PreferLocalFile), config()->lineLengthLimit(), m_buffer->longestLineLoaded()), KTextEditor::Message::Warning); QAction *increaseAndReload = new QAction(i18n("Temporarily raise limit and reload file"), message); connect(increaseAndReload, SIGNAL(triggered()), this, SLOT(openWithLineLengthLimitOverride())); message->addAction(increaseAndReload, true); message->addAction(new QAction(i18n("Close"), message), true); message->setWordWrap(true); postMessage(message); // remember error m_openingError = true; m_openingErrorMessage = i18n("The file %1 was opened and contained lines longer than the configured Line Length Limit (%2 characters).
" "The longest of those lines was %3 characters long
" "Those lines were wrapped and the document is set to read-only mode, as saving will modify its content.", this->url().toDisplayString(QUrl::PreferLocalFile), config()->lineLengthLimit(),m_buffer->longestLineLoaded()); } // // return the success // return success; } bool KTextEditor::DocumentPrivate::saveFile() { // delete pending mod-on-hd message if applicable. delete m_modOnHdHandler; // some warnings, if file was changed by the outside! if (!url().isEmpty()) { if (m_fileChangedDialogsActivated && m_modOnHd) { QString str = reasonedMOHString() + QLatin1String("\n\n"); if (!isModified()) { if (KMessageBox::warningContinueCancel(dialogParent(), str + i18n("Do you really want to save this unmodified file? You could overwrite changed data in the file on disk."), i18n("Trying to Save Unmodified File"), KGuiItem(i18n("Save Nevertheless"))) != KMessageBox::Continue) { return false; } } else { if (KMessageBox::warningContinueCancel(dialogParent(), str + i18n("Do you really want to save this file? Both your open file and the file on disk were changed. There could be some data lost."), i18n("Possible Data Loss"), KGuiItem(i18n("Save Nevertheless"))) != KMessageBox::Continue) { return false; } } } } // // can we encode it if we want to save it ? // if (!m_buffer->canEncode() && (KMessageBox::warningContinueCancel(dialogParent(), i18n("The selected encoding cannot encode every Unicode character in this document. Do you really want to save it? There could be some data lost."), i18n("Possible Data Loss"), KGuiItem(i18n("Save Nevertheless"))) != KMessageBox::Continue)) { return false; } /** * create a backup file or abort if that fails! * if no backup file wanted, this routine will just return true */ if (!createBackupFile()) return false; // update file type, pass no file path, read file type content from this document QString oldPath = m_dirWatchFile; // only update file type if path has changed so that variables are not overridden on normal save if (oldPath != localFilePath()) { updateFileType(KTextEditor::EditorPrivate::self()->modeManager()->fileType(this, QString())); if (url().isLocalFile()) { // if file is local then read dir config for new path readDirConfig(); } } // read our vars readVariables(); // remove file from dirwatch deactivateDirWatch(); // remove all trailing spaces in the document (as edit actions) // NOTE: we need this as edit actions, since otherwise the edit actions // in the swap file recovery may happen at invalid cursor positions removeTrailingSpaces(); // // try to save // if (!m_buffer->saveFile(localFilePath())) { // add m_file again to dirwatch activateDirWatch(oldPath); KMessageBox::error(dialogParent(), i18n("The document could not be saved, as it was not possible to write to %1.\n\nCheck that you have write access to this file or that enough disk space is available.", this->url().toDisplayString(QUrl::PreferLocalFile))); return false; } // update the checksum createDigest(); // add m_file again to dirwatch activateDirWatch(); // // we are not modified // if (m_modOnHd) { m_modOnHd = false; m_modOnHdReason = OnDiskUnmodified; m_prevModOnHdReason = OnDiskUnmodified; emit modifiedOnDisk(this, m_modOnHd, m_modOnHdReason); } // (dominik) mark last undo group as not mergeable, otherwise the next // edit action might be merged and undo will never stop at the saved state m_undoManager->undoSafePoint(); m_undoManager->updateLineModifications(); // // return success // return true; } bool KTextEditor::DocumentPrivate::createBackupFile() { /** * backup for local or remote files wanted? */ const bool backupLocalFiles = (config()->backupFlags() & KateDocumentConfig::LocalFiles); const bool backupRemoteFiles = (config()->backupFlags() & KateDocumentConfig::RemoteFiles); /** * early out, before mount check: backup wanted at all? * => if not, all fine, just return */ if (!backupLocalFiles && !backupRemoteFiles) { return true; } /** * decide if we need backup based on locality * skip that, if we always want backups, as currentMountPoints is not that fast */ QUrl u(url()); bool needBackup = backupLocalFiles && backupRemoteFiles; if (!needBackup) { bool slowOrRemoteFile = !u.isLocalFile(); if (!slowOrRemoteFile) { // could be a mounted remote filesystem (e.g. nfs, sshfs, cifs) // we have the early out above to skip this, if we want no backup, which is the default KMountPoint::Ptr mountPoint = KMountPoint::currentMountPoints().findByDevice(u.toLocalFile()); slowOrRemoteFile = (mountPoint && mountPoint->probablySlow()); } needBackup = (!slowOrRemoteFile && backupLocalFiles) || (slowOrRemoteFile && backupRemoteFiles); } /** * no backup needed? be done */ if (!needBackup) { return true; } /** * else: try to backup */ if (config()->backupPrefix().contains(QDir::separator())) { /** * replace complete path, as prefix is a path! */ u.setPath(config()->backupPrefix() + u.fileName() + config()->backupSuffix()); } else { /** * replace filename in url */ const QString fileName = u.fileName(); u = u.adjusted(QUrl::RemoveFilename); u.setPath(u.path() + config()->backupPrefix() + fileName + config()->backupSuffix()); } qCDebug(LOG_KTE) << "backup src file name: " << url(); qCDebug(LOG_KTE) << "backup dst file name: " << u; // handle the backup... bool backupSuccess = false; // local file mode, no kio if (u.isLocalFile()) { if (QFile::exists(url().toLocalFile())) { // first: check if backupFile is already there, if true, unlink it QFile backupFile(u.toLocalFile()); if (backupFile.exists()) { backupFile.remove(); } backupSuccess = QFile::copy(url().toLocalFile(), u.toLocalFile()); } else { backupSuccess = true; } } else { // remote file mode, kio // get the right permissions, start with safe default KIO::StatJob *statJob = KIO::stat(url(), KIO::StatJob::SourceSide, 2); KJobWidgets::setWindow(statJob, QApplication::activeWindow()); if (statJob->exec()) { // do a evil copy which will overwrite target if possible KFileItem item(statJob->statResult(), url()); KIO::FileCopyJob *job = KIO::file_copy(url(), u, item.permissions(), KIO::Overwrite); KJobWidgets::setWindow(job, QApplication::activeWindow()); backupSuccess = job->exec(); } else { backupSuccess = true; } } // backup has failed, ask user how to proceed if (!backupSuccess && (KMessageBox::warningContinueCancel(dialogParent() , i18n("For file %1 no backup copy could be created before saving." " If an error occurs while saving, you might lose the data of this file." " A reason could be that the media you write to is full or the directory of the file is read-only for you.", url().toDisplayString(QUrl::PreferLocalFile)) , i18n("Failed to create backup copy.") , KGuiItem(i18n("Try to Save Nevertheless")) , KStandardGuiItem::cancel(), QStringLiteral("Backup Failed Warning")) != KMessageBox::Continue)) { return false; } return true; } void KTextEditor::DocumentPrivate::readDirConfig() { if (!url().isLocalFile()) { return; } /** * first search .kateconfig upwards * with recursion guard */ QSet seenDirectories; QDir dir (QFileInfo(localFilePath()).absolutePath()); while (!seenDirectories.contains (dir.absolutePath ())) { /** * fill recursion guard */ seenDirectories.insert (dir.absolutePath ()); // try to open config file in this dir QFile f(dir.absolutePath () + QLatin1String("/.kateconfig")); if (f.open(QIODevice::ReadOnly)) { QTextStream stream(&f); uint linesRead = 0; QString line = stream.readLine(); while ((linesRead < 32) && !line.isNull()) { readVariableLine(line); line = stream.readLine(); linesRead++; } return; } /** * else: cd up, if possible or abort */ if (!dir.cdUp()) { break; } } #if EDITORCONFIG_FOUND // if there wasn’t any .kateconfig file and KTextEditor was compiled with // EditorConfig support, try to load document config from a .editorconfig // file, if such is provided EditorConfig editorConfig(this); editorConfig.parse(); #endif } void KTextEditor::DocumentPrivate::activateDirWatch(const QString &useFileName) { QString fileToUse = useFileName; if (fileToUse.isEmpty()) { fileToUse = localFilePath(); } QFileInfo fileInfo = QFileInfo(fileToUse); if (fileInfo.isSymLink()) { // Monitor the actual data and not the symlink fileToUse = fileInfo.canonicalFilePath(); } // same file as we are monitoring, return if (fileToUse == m_dirWatchFile) { return; } // remove the old watched file deactivateDirWatch(); // add new file if needed if (url().isLocalFile() && !fileToUse.isEmpty()) { KTextEditor::EditorPrivate::self()->dirWatch()->addFile(fileToUse); m_dirWatchFile = fileToUse; } } void KTextEditor::DocumentPrivate::deactivateDirWatch() { if (!m_dirWatchFile.isEmpty()) { KTextEditor::EditorPrivate::self()->dirWatch()->removeFile(m_dirWatchFile); } m_dirWatchFile.clear(); } bool KTextEditor::DocumentPrivate::openUrl(const QUrl &url) { if (!m_reloading) { // Reset filetype when opening url m_fileTypeSetByUser = false; } bool res = KTextEditor::Document::openUrl(normalizeUrl(url)); updateDocName(); return res; } bool KTextEditor::DocumentPrivate::closeUrl() { // // file mod on hd // if (!m_reloading && !url().isEmpty()) { if (m_fileChangedDialogsActivated && m_modOnHd) { // make sure to not forget pending mod-on-hd handler delete m_modOnHdHandler; QWidget *parentWidget(dialogParent()); if (!(KMessageBox::warningContinueCancel( parentWidget, reasonedMOHString() + QLatin1String("\n\n") + i18n("Do you really want to continue to close this file? Data loss may occur."), i18n("Possible Data Loss"), KGuiItem(i18n("Close Nevertheless")), KStandardGuiItem::cancel(), QStringLiteral("kate_close_modonhd_%1").arg(m_modOnHdReason)) == KMessageBox::Continue)) { /** * reset reloading */ m_reloading = false; return false; } } } // // first call the normal kparts implementation // if (!KParts::ReadWritePart::closeUrl()) { /** * reset reloading */ m_reloading = false; return false; } // Tell the world that we're about to go ahead with the close if (!m_reloading) { emit aboutToClose(this); } /** * delete all KTE::Messages */ if (!m_messageHash.isEmpty()) { QList keys = m_messageHash.keys(); foreach (KTextEditor::Message *message, keys) { delete message; } } /** * we are about to invalidate all cursors/ranges/.. => m_buffer->clear will do so */ emit aboutToInvalidateMovingInterfaceContent(this); // remove file from dirwatch deactivateDirWatch(); // // empty url + fileName // setUrl(QUrl()); setLocalFilePath(QString()); // we are not modified if (m_modOnHd) { m_modOnHd = false; m_modOnHdReason = OnDiskUnmodified; m_prevModOnHdReason = OnDiskUnmodified; emit modifiedOnDisk(this, m_modOnHd, m_modOnHdReason); } // remove all marks clearMarks(); // clear the buffer m_buffer->clear(); // clear undo/redo history m_undoManager->clearUndo(); m_undoManager->clearRedo(); // no, we are no longer modified setModified(false); // we have no longer any hl m_buffer->setHighlight(0); // update all our views foreach (KTextEditor::ViewPrivate *view, m_views) { view->clearSelection(); // fix bug #118588 view->clear(); } // purge swap file if (m_swapfile) { m_swapfile->fileClosed(); } // success return true; } bool KTextEditor::DocumentPrivate::isDataRecoveryAvailable() const { return m_swapfile && m_swapfile->shouldRecover(); } void KTextEditor::DocumentPrivate::recoverData() { if (isDataRecoveryAvailable()) { m_swapfile->recover(); } } void KTextEditor::DocumentPrivate::discardDataRecovery() { if (isDataRecoveryAvailable()) { m_swapfile->discard(); } } void KTextEditor::DocumentPrivate::setReadWrite(bool rw) { if (isReadWrite() == rw) { return; } KParts::ReadWritePart::setReadWrite(rw); foreach (KTextEditor::ViewPrivate *view, m_views) { view->slotUpdateUndo(); view->slotReadWriteChanged(); } emit readWriteChanged(this); } void KTextEditor::DocumentPrivate::setModified(bool m) { if (isModified() != m) { KParts::ReadWritePart::setModified(m); foreach (KTextEditor::ViewPrivate *view, m_views) { view->slotUpdateUndo(); } emit modifiedChanged(this); } m_undoManager->setModified(m); } //END //BEGIN Kate specific stuff ;) void KTextEditor::DocumentPrivate::makeAttribs(bool needInvalidate) { foreach (KTextEditor::ViewPrivate *view, m_views) { view->renderer()->updateAttributes(); } if (needInvalidate) { m_buffer->invalidateHighlighting(); } foreach (KTextEditor::ViewPrivate *view, m_views) { view->tagAll(); view->updateView(true); } } // the attributes of a hl have changed, update void KTextEditor::DocumentPrivate::internalHlChanged() { makeAttribs(); } void KTextEditor::DocumentPrivate::addView(KTextEditor::View *view) { Q_ASSERT (!m_views.contains(view)); m_views.insert(view, static_cast(view)); // apply the view & renderer vars from the file type if (!m_fileType.isEmpty()) { readVariableLine(KTextEditor::EditorPrivate::self()->modeManager()->fileType(m_fileType).varLine, true); } // apply the view & renderer vars from the file readVariables(true); setActiveView(view); } void KTextEditor::DocumentPrivate::removeView(KTextEditor::View *view) { Q_ASSERT (m_views.contains(view)); m_views.remove(view); if (activeView() == view) { setActiveView(nullptr); } } void KTextEditor::DocumentPrivate::setActiveView(KTextEditor::View *view) { if (m_activeView == view) { return; } m_activeView = static_cast(view); } bool KTextEditor::DocumentPrivate::ownedView(KTextEditor::ViewPrivate *view) { // do we own the given view? return (m_views.contains(view)); } int KTextEditor::DocumentPrivate::toVirtualColumn(int line, int column) const { Kate::TextLine textLine = m_buffer->plainLine(line); if (textLine) { return textLine->toVirtualColumn(column, config()->tabWidth()); } else { return 0; } } int KTextEditor::DocumentPrivate::toVirtualColumn(const KTextEditor::Cursor &cursor) const { return toVirtualColumn(cursor.line(), cursor.column()); } int KTextEditor::DocumentPrivate::fromVirtualColumn(int line, int column) const { Kate::TextLine textLine = m_buffer->plainLine(line); if (textLine) { return textLine->fromVirtualColumn(column, config()->tabWidth()); } else { return 0; } } int KTextEditor::DocumentPrivate::fromVirtualColumn(const KTextEditor::Cursor &cursor) const { return fromVirtualColumn(cursor.line(), cursor.column()); } bool KTextEditor::DocumentPrivate::typeChars(KTextEditor::ViewPrivate *view, const QString &realChars) { /** * filter out non-printable chars (convert to utf-32 to support surrogate pairs) */ const auto realUcs4Chars = realChars.toUcs4(); QVector ucs4Chars; Q_FOREACH (auto c, realUcs4Chars) if (QChar::isPrint(c) || c == QChar::fromLatin1('\t') || c == QChar::fromLatin1('\n') || c == QChar::fromLatin1('\r')) { ucs4Chars.append(c); } QString chars = QString::fromUcs4(ucs4Chars.data(), ucs4Chars.size()); /** * no printable chars => nothing to insert! */ if (chars.isEmpty()) { return false; } /** * auto bracket handling for newly inserted text * remember if we should auto add some */ QChar closingBracket; if (view->config()->autoBrackets() && chars.size() == 1) { /** * we inserted a bracket? * => remember the matching closing one */ closingBracket = matchingEndBracket(chars[0], true); /** * closing bracket for the autobracket we inserted earlier? */ if ( m_currentAutobraceClosingChar == chars[0] && m_currentAutobraceRange ) { // do nothing m_currentAutobraceRange.reset(nullptr); view->cursorRight(); return true; } } /** * selection around => special handling if we want to add auto brackets */ if (view->selection() && !closingBracket.isNull()) { /** * add bracket at start + end of the selection */ KTextEditor::Cursor oldCur = view->cursorPosition(); insertText(view->selectionRange().start(), chars); view->slotTextInserted(view, oldCur, chars); view->setCursorPosition(view->selectionRange().end()); oldCur = view->cursorPosition(); insertText(view->selectionRange().end(), QString(closingBracket)); view->slotTextInserted(view, oldCur, QString(closingBracket)); /** * expand selection */ view->setSelection(KTextEditor::Range(view->selectionRange().start() + Cursor{0, 1}, view->cursorPosition() - Cursor{0, 1})); view->setCursorPosition(view->selectionRange().start()); } /** * else normal handling */ else { editStart(); if (!view->config()->persistentSelection() && view->selection()) { view->removeSelectedText(); } const KTextEditor::Cursor oldCur(view->cursorPosition()); const bool multiLineBlockMode = view->blockSelection() && view->selection(); if (view->currentInputMode()->overwrite()) { // blockmode multiline selection case: remove chars in every line const KTextEditor::Range selectionRange = view->selectionRange(); const int startLine = multiLineBlockMode ? qMax(0, selectionRange.start().line()) : view->cursorPosition().line(); const int endLine = multiLineBlockMode ? qMin(selectionRange.end().line(), lastLine()) : startLine; const int virtualColumn = toVirtualColumn(multiLineBlockMode ? selectionRange.end() : view->cursorPosition()); for (int line = endLine; line >= startLine; --line) { Kate::TextLine textLine = m_buffer->plainLine(line); Q_ASSERT(textLine); const int column = fromVirtualColumn(line, virtualColumn); KTextEditor::Range r = KTextEditor::Range(KTextEditor::Cursor(line, column), qMin(chars.length(), textLine->length() - column)); // replace mode needs to know what was removed so it can be restored with backspace if (oldCur.column() < lineLength(line)) { QChar removed = characterAt(KTextEditor::Cursor(line, column)); view->currentInputMode()->overwrittenChar(removed); } removeText(r); } } if (multiLineBlockMode) { KTextEditor::Range selectionRange = view->selectionRange(); const int startLine = qMax(0, selectionRange.start().line()); const int endLine = qMin(selectionRange.end().line(), lastLine()); const int column = toVirtualColumn(selectionRange.end()); for (int line = endLine; line >= startLine; --line) { editInsertText(line, fromVirtualColumn(line, column), chars); } int newSelectionColumn = toVirtualColumn(view->cursorPosition()); selectionRange.setRange(KTextEditor::Cursor(selectionRange.start().line(), fromVirtualColumn(selectionRange.start().line(), newSelectionColumn)) , KTextEditor::Cursor(selectionRange.end().line(), fromVirtualColumn(selectionRange.end().line(), newSelectionColumn))); view->setSelection(selectionRange); } else { chars = eventuallyReplaceTabs(view->cursorPosition(), chars); insertText(view->cursorPosition(), chars); } /** * auto bracket handling for newly inserted text * we inserted a bracket? * => add the matching closing one to the view + input chars * try to preserve the cursor position */ bool skipAutobrace = closingBracket == QLatin1Char('\''); if ( highlight() && skipAutobrace ) { auto context = highlight()->contextForLocation(this, view->cursorPosition() - Cursor{0, 1}); // skip adding ' in spellchecked areas, because those are text skipAutobrace = !context || highlight()->attributeRequiresSpellchecking(context->attr); } if (!closingBracket.isNull() && !skipAutobrace ) { // add bracket to the view const auto cursorPos(view->cursorPosition()); const auto nextChar = view->document()->text({cursorPos, cursorPos + Cursor{0, 1}}).trimmed(); if ( nextChar.isEmpty() || ! nextChar.at(0).isLetterOrNumber() ) { insertText(view->cursorPosition(), QString(closingBracket)); const auto insertedAt(view->cursorPosition()); view->setCursorPosition(cursorPos); m_currentAutobraceRange.reset(newMovingRange({cursorPos - Cursor{0, 1}, insertedAt}, KTextEditor::MovingRange::DoNotExpand)); connect(view, &View::cursorPositionChanged, this, &DocumentPrivate::checkCursorForAutobrace, Qt::UniqueConnection); // add bracket to chars inserted! needed for correct signals + indent chars.append(closingBracket); } m_currentAutobraceClosingChar = closingBracket; } // end edit session here, to have updated HL in userTypedChar! editEnd(); // trigger indentation KTextEditor::Cursor b(view->cursorPosition()); m_indenter->userTypedChar(view, b, chars.isEmpty() ? QChar() : chars.at(chars.length() - 1)); /** * inform the view about the original inserted chars */ view->slotTextInserted(view, oldCur, chars); } /** * be done */ return true; } void KTextEditor::DocumentPrivate::checkCursorForAutobrace(KTextEditor::View*, const KTextEditor::Cursor& newPos) { if ( m_currentAutobraceRange && ! m_currentAutobraceRange->toRange().contains(newPos) ) { m_currentAutobraceRange.clear(); } } void KTextEditor::DocumentPrivate::newLine(KTextEditor::ViewPrivate *v) { editStart(); if (!v->config()->persistentSelection() && v->selection()) { v->removeSelectedText(); v->clearSelection(); } // query cursor position KTextEditor::Cursor c = v->cursorPosition(); - if (c.line() > (int)lastLine()) { + if (c.line() > lastLine()) { c.setLine(lastLine()); } if (c.line() < 0) { c.setLine(0); } - uint ln = c.line(); + int ln = c.line(); Kate::TextLine textLine = plainKateTextLine(ln); - if (c.column() > (int)textLine->length()) { + if (c.column() > textLine->length()) { c.setColumn(textLine->length()); } // first: wrap line editWrapLine(c.line(), c.column()); // end edit session here, to have updated HL in userTypedChar! editEnd(); // second: indent the new line, if needed... m_indenter->userTypedChar(v, v->cursorPosition(), QLatin1Char('\n')); } void KTextEditor::DocumentPrivate::transpose(const KTextEditor::Cursor &cursor) { Kate::TextLine textLine = m_buffer->plainLine(cursor.line()); if (!textLine || (textLine->length() < 2)) { return; } uint col = cursor.column(); if (col > 0) { col--; } if ((textLine->length() - col) < 2) { return; } uint line = cursor.line(); QString s; //clever swap code if first character on the line swap right&left //otherwise left & right s.append(textLine->at(col + 1)); s.append(textLine->at(col)); //do the swap // do it right, never ever manipulate a textline editStart(); editRemoveText(line, col, 2); editInsertText(line, col, s); editEnd(); } void KTextEditor::DocumentPrivate::backspace(KTextEditor::ViewPrivate *view, const KTextEditor::Cursor &c) { if (!view->config()->persistentSelection() && view->selection()) { if (view->blockSelection() && view->selection() && toVirtualColumn(view->selectionRange().start()) == toVirtualColumn(view->selectionRange().end())) { // Remove one character after selection line KTextEditor::Range range = view->selectionRange(); range.setStart(KTextEditor::Cursor(range.start().line(), range.start().column() - 1)); view->setSelection(range); } view->removeSelectedText(); return; } uint col = qMax(c.column(), 0); uint line = qMax(c.line(), 0); if ((col == 0) && (line == 0)) { return; } if (col > 0) { bool useNextBlock = false; if (config()->backspaceIndents()) { // backspace indents: erase to next indent position Kate::TextLine textLine = m_buffer->plainLine(line); // don't forget this check!!!! really!!!! if (!textLine) { return; } int colX = textLine->toVirtualColumn(col, config()->tabWidth()); int pos = textLine->firstChar(); if (pos > 0) { pos = textLine->toVirtualColumn(pos, config()->tabWidth()); } if (pos < 0 || pos >= (int)colX) { // only spaces on left side of cursor indent(KTextEditor::Range(line, 0, line, 0), -1); } else { useNextBlock = true; } } if (!config()->backspaceIndents() || useNextBlock) { KTextEditor::Cursor beginCursor(line, 0); KTextEditor::Cursor endCursor(line, col); if (!view->config()->backspaceRemoveComposed()) { // Normal backspace behavior beginCursor.setColumn(col - 1); // move to left of surrogate pair if (!isValidTextPosition(beginCursor)) { Q_ASSERT(col >= 2); beginCursor.setColumn(col - 2); } } else { beginCursor.setColumn(view->textLayout(c)->previousCursorPosition(c.column())); } removeText(KTextEditor::Range(beginCursor, endCursor)); // in most cases cursor is moved by removeText, but we should do it manually // for past-end-of-line cursors in block mode view->setCursorPosition(beginCursor); } } else { // col == 0: wrap to previous line if (line >= 1) { Kate::TextLine textLine = m_buffer->plainLine(line - 1); // don't forget this check!!!! really!!!! if (!textLine) { return; } if (config()->wordWrap() && textLine->endsWith(QLatin1String(" "))) { // gg: in hard wordwrap mode, backspace must also eat the trailing space removeText(KTextEditor::Range(line - 1, textLine->length() - 1, line, 0)); } else { removeText(KTextEditor::Range(line - 1, textLine->length(), line, 0)); } } } if ( m_currentAutobraceRange ) { const auto r = m_currentAutobraceRange->toRange(); if ( r.columnWidth() == 1 && view->cursorPosition() == r.start() ) { // start parenthesis removed and range length is 1, remove end as well del(view, view->cursorPosition()); m_currentAutobraceRange.clear(); } } } void KTextEditor::DocumentPrivate::del(KTextEditor::ViewPrivate *view, const KTextEditor::Cursor &c) { if (!view->config()->persistentSelection() && view->selection()) { if (view->blockSelection() && view->selection() && toVirtualColumn(view->selectionRange().start()) == toVirtualColumn(view->selectionRange().end())) { // Remove one character after selection line KTextEditor::Range range = view->selectionRange(); range.setEnd(KTextEditor::Cursor(range.end().line(), range.end().column() + 1)); view->setSelection(range); } view->removeSelectedText(); return; } if (c.column() < (int) m_buffer->plainLine(c.line())->length()) { KTextEditor::Cursor endCursor(c.line(), view->textLayout(c)->nextCursorPosition(c.column())); removeText(KTextEditor::Range(c, endCursor)); } else if (c.line() < lastLine()) { removeText(KTextEditor::Range(c.line(), c.column(), c.line() + 1, 0)); } } void KTextEditor::DocumentPrivate::paste(KTextEditor::ViewPrivate *view, const QString &text) { static const QChar newLineChar(QLatin1Char('\n')); QString s = text; if (s.isEmpty()) { return; } int lines = s.count(newLineChar); m_undoManager->undoSafePoint(); editStart(); KTextEditor::Cursor pos = view->cursorPosition(); if (!view->config()->persistentSelection() && view->selection()) { pos = view->selectionRange().start(); if (view->blockSelection()) { pos = rangeOnLine(view->selectionRange(), pos.line()).start(); if (lines == 0) { s += newLineChar; s = s.repeated(view->selectionRange().numberOfLines() + 1); s.chop(1); } } view->removeSelectedText(); } if (config()->ovr()) { QStringList pasteLines = s.split(newLineChar); if (!view->blockSelection()) { int endColumn = (pasteLines.count() == 1 ? pos.column() : 0) + pasteLines.last().length(); removeText(KTextEditor::Range(pos, pos.line() + pasteLines.count() - 1, endColumn)); } else { int maxi = qMin(pos.line() + pasteLines.count(), this->lines()); for (int i = pos.line(); i < maxi; ++i) { int pasteLength = pasteLines.at(i - pos.line()).length(); removeText(KTextEditor::Range(i, pos.column(), i, qMin(pasteLength + pos.column(), lineLength(i)))); } } } insertText(pos, s, view->blockSelection()); editEnd(); // move cursor right for block select, as the user is moved right internal // even in that case, but user expects other behavior in block selection // mode ! // just let cursor stay, that was it before I changed to moving ranges! if (view->blockSelection()) { view->setCursorPositionInternal(pos); } if (config()->indentPastedText()) { KTextEditor::Range range = KTextEditor::Range(KTextEditor::Cursor(pos.line(), 0), KTextEditor::Cursor(pos.line() + lines, 0)); m_indenter->indent(view, range); } if (!view->blockSelection()) { emit charactersSemiInteractivelyInserted(pos, s); } m_undoManager->undoSafePoint(); } void KTextEditor::DocumentPrivate::indent(KTextEditor::Range range, int change) { if (!isReadWrite()) { return; } editStart(); m_indenter->changeIndent(range, change); editEnd(); } void KTextEditor::DocumentPrivate::align(KTextEditor::ViewPrivate *view, const KTextEditor::Range &range) { m_indenter->indent(view, range); } void KTextEditor::DocumentPrivate::insertTab(KTextEditor::ViewPrivate *view, const KTextEditor::Cursor &) { if (!isReadWrite()) { return; } int lineLen = line(view->cursorPosition().line()).length(); KTextEditor::Cursor c = view->cursorPosition(); editStart(); if (!view->config()->persistentSelection() && view->selection()) { view->removeSelectedText(); } else if (view->currentInputMode()->overwrite() && c.column() < lineLen) { KTextEditor::Range r = KTextEditor::Range(view->cursorPosition(), 1); // replace mode needs to know what was removed so it can be restored with backspace QChar removed = line(view->cursorPosition().line()).at(r.start().column()); view->currentInputMode()->overwrittenChar(removed); removeText(r); } c = view->cursorPosition(); editInsertText(c.line(), c.column(), QStringLiteral("\t")); editEnd(); } /* Remove a given string at the beginning of the current line. */ bool KTextEditor::DocumentPrivate::removeStringFromBeginning(int line, const QString &str) { Kate::TextLine textline = m_buffer->plainLine(line); KTextEditor::Cursor cursor(line, 0); bool there = textline->startsWith(str); if (!there) { cursor.setColumn(textline->firstChar()); there = textline->matchesAt(cursor.column(), str); } if (there) { // Remove some chars removeText(KTextEditor::Range(cursor, str.length())); } return there; } /* Remove a given string at the end of the current line. */ bool KTextEditor::DocumentPrivate::removeStringFromEnd(int line, const QString &str) { Kate::TextLine textline = m_buffer->plainLine(line); KTextEditor::Cursor cursor(line, 0); bool there = textline->endsWith(str); if (there) { cursor.setColumn(textline->length() - str.length()); } else { cursor.setColumn(textline->lastChar() - str.length() + 1); there = textline->matchesAt(cursor.column(), str); } if (there) { // Remove some chars removeText(KTextEditor::Range(cursor, str.length())); } return there; } /* Replace tabs by spaces in the given string, if enabled. */ QString KTextEditor::DocumentPrivate::eventuallyReplaceTabs(const KTextEditor::Cursor &cursorPos, const QString &str) const { const bool replacetabs = config()->replaceTabsDyn(); if ( ! replacetabs ) { return str; } const int indentWidth = config()->indentationWidth(); static const QLatin1Char tabChar('\t'); int column = cursorPos.column(); // The result will always be at least as long as the input QString result; result.reserve(str.size()); Q_FOREACH (const QChar ch, str) { if (ch == tabChar) { // Insert only enough spaces to align to the next indentWidth column // This fixes bug #340212 int spacesToInsert = indentWidth - (column % indentWidth); result += QStringLiteral(" ").repeated(spacesToInsert); column += spacesToInsert; } else { // Just keep all other typed characters as-is result += ch; ++column; } } return result; } /* Add to the current line a comment line mark at the beginning. */ void KTextEditor::DocumentPrivate::addStartLineCommentToSingleLine(int line, int attrib) { QString commentLineMark = highlight()->getCommentSingleLineStart(attrib); int pos = -1; if (highlight()->getCommentSingleLinePosition(attrib) == KateHighlighting::CSLPosColumn0) { pos = 0; commentLineMark += QLatin1Char(' '); } else { const Kate::TextLine l = kateTextLine(line); pos = l->firstChar(); } if (pos >= 0) { insertText(KTextEditor::Cursor(line, pos), commentLineMark); } } /* Remove from the current line a comment line mark at the beginning if there is one. */ bool KTextEditor::DocumentPrivate::removeStartLineCommentFromSingleLine(int line, int attrib) { const QString shortCommentMark = highlight()->getCommentSingleLineStart(attrib); const QString longCommentMark = shortCommentMark + QLatin1Char(' '); editStart(); // Try to remove the long comment mark first bool removed = (removeStringFromBeginning(line, longCommentMark) || removeStringFromBeginning(line, shortCommentMark)); editEnd(); return removed; } /* Add to the current line a start comment mark at the beginning and a stop comment mark at the end. */ void KTextEditor::DocumentPrivate::addStartStopCommentToSingleLine(int line, int attrib) { const QString startCommentMark = highlight()->getCommentStart(attrib) + QLatin1Char(' '); const QString stopCommentMark = QLatin1Char(' ') + highlight()->getCommentEnd(attrib); editStart(); // Add the start comment mark insertText(KTextEditor::Cursor(line, 0), startCommentMark); // Go to the end of the line const int col = m_buffer->plainLine(line)->length(); // Add the stop comment mark insertText(KTextEditor::Cursor(line, col), stopCommentMark); editEnd(); } /* Remove from the current line a start comment mark at the beginning and a stop comment mark at the end. */ bool KTextEditor::DocumentPrivate::removeStartStopCommentFromSingleLine(int line, int attrib) { const QString shortStartCommentMark = highlight()->getCommentStart(attrib); const QString longStartCommentMark = shortStartCommentMark + QLatin1Char(' '); const QString shortStopCommentMark = highlight()->getCommentEnd(attrib); const QString longStopCommentMark = QLatin1Char(' ') + shortStopCommentMark; editStart(); // Try to remove the long start comment mark first const bool removedStart = (removeStringFromBeginning(line, longStartCommentMark) || removeStringFromBeginning(line, shortStartCommentMark)); // Try to remove the long stop comment mark first const bool removedStop = removedStart && (removeStringFromEnd(line, longStopCommentMark) || removeStringFromEnd(line, shortStopCommentMark)); editEnd(); return (removedStart || removedStop); } /* Add to the current selection a start comment mark at the beginning and a stop comment mark at the end. */ void KTextEditor::DocumentPrivate::addStartStopCommentToSelection(KTextEditor::ViewPrivate *view, int attrib) { const QString startComment = highlight()->getCommentStart(attrib); const QString endComment = highlight()->getCommentEnd(attrib); KTextEditor::Range range = view->selectionRange(); if ((range.end().column() == 0) && (range.end().line() > 0)) { range.setEnd(KTextEditor::Cursor(range.end().line() - 1, lineLength(range.end().line() - 1))); } editStart(); if (!view->blockSelection()) { insertText(range.end(), endComment); insertText(range.start(), startComment); } else { for (int line = range.start().line(); line <= range.end().line(); line++) { KTextEditor::Range subRange = rangeOnLine(range, line); insertText(subRange.end(), endComment); insertText(subRange.start(), startComment); } } editEnd(); // selection automatically updated (MovingRange) } /* Add to the current selection a comment line mark at the beginning of each line. */ void KTextEditor::DocumentPrivate::addStartLineCommentToSelection(KTextEditor::ViewPrivate *view, int attrib) { const QString commentLineMark = highlight()->getCommentSingleLineStart(attrib) + QLatin1Char(' '); int sl = view->selectionRange().start().line(); int el = view->selectionRange().end().line(); // if end of selection is in column 0 in last line, omit the last line if ((view->selectionRange().end().column() == 0) && (el > 0)) { el--; } editStart(); // For each line of the selection for (int z = el; z >= sl; z--) { //insertText (z, 0, commentLineMark); addStartLineCommentToSingleLine(z, attrib); } editEnd(); // selection automatically updated (MovingRange) } bool KTextEditor::DocumentPrivate::nextNonSpaceCharPos(int &line, int &col) { for (; line < (int)m_buffer->count(); line++) { Kate::TextLine textLine = m_buffer->plainLine(line); if (!textLine) { break; } col = textLine->nextNonSpaceChar(col); if (col != -1) { return true; // Next non-space char found } col = 0; } // No non-space char found line = -1; col = -1; return false; } bool KTextEditor::DocumentPrivate::previousNonSpaceCharPos(int &line, int &col) { while (true) { Kate::TextLine textLine = m_buffer->plainLine(line); if (!textLine) { break; } col = textLine->previousNonSpaceChar(col); if (col != -1) { return true; } if (line == 0) { return false; } --line; col = textLine->length(); } // No non-space char found line = -1; col = -1; return false; } /* Remove from the selection a start comment mark at the beginning and a stop comment mark at the end. */ bool KTextEditor::DocumentPrivate::removeStartStopCommentFromSelection(KTextEditor::ViewPrivate *view, int attrib) { const QString startComment = highlight()->getCommentStart(attrib); const QString endComment = highlight()->getCommentEnd(attrib); int sl = qMax (0, view->selectionRange().start().line()); int el = qMin (view->selectionRange().end().line(), lastLine()); int sc = view->selectionRange().start().column(); int ec = view->selectionRange().end().column(); // The selection ends on the char before selectEnd if (ec != 0) { --ec; } else if (el > 0) { --el; ec = m_buffer->plainLine(el)->length() - 1; } const int startCommentLen = startComment.length(); const int endCommentLen = endComment.length(); // had this been perl or sed: s/^\s*$startComment(.+?)$endComment\s*/$2/ bool remove = nextNonSpaceCharPos(sl, sc) && m_buffer->plainLine(sl)->matchesAt(sc, startComment) && previousNonSpaceCharPos(el, ec) && ((ec - endCommentLen + 1) >= 0) && m_buffer->plainLine(el)->matchesAt(ec - endCommentLen + 1, endComment); if (remove) { editStart(); removeText(KTextEditor::Range(el, ec - endCommentLen + 1, el, ec + 1)); removeText(KTextEditor::Range(sl, sc, sl, sc + startCommentLen)); editEnd(); // selection automatically updated (MovingRange) } return remove; } bool KTextEditor::DocumentPrivate::removeStartStopCommentFromRegion(const KTextEditor::Cursor &start, const KTextEditor::Cursor &end, int attrib) { const QString startComment = highlight()->getCommentStart(attrib); const QString endComment = highlight()->getCommentEnd(attrib); const int startCommentLen = startComment.length(); const int endCommentLen = endComment.length(); const bool remove = m_buffer->plainLine(start.line())->matchesAt(start.column(), startComment) && m_buffer->plainLine(end.line())->matchesAt(end.column() - endCommentLen, endComment); if (remove) { editStart(); removeText(KTextEditor::Range(end.line(), end.column() - endCommentLen, end.line(), end.column())); removeText(KTextEditor::Range(start, startCommentLen)); editEnd(); } return remove; } /* Remove from the beginning of each line of the selection a start comment line mark. */ bool KTextEditor::DocumentPrivate::removeStartLineCommentFromSelection(KTextEditor::ViewPrivate *view, int attrib) { const QString shortCommentMark = highlight()->getCommentSingleLineStart(attrib); const QString longCommentMark = shortCommentMark + QLatin1Char(' '); int sl = view->selectionRange().start().line(); int el = view->selectionRange().end().line(); if ((view->selectionRange().end().column() == 0) && (el > 0)) { el--; } bool removed = false; editStart(); // For each line of the selection for (int z = el; z >= sl; z--) { // Try to remove the long comment mark first removed = (removeStringFromBeginning(z, longCommentMark) || removeStringFromBeginning(z, shortCommentMark) || removed); } editEnd(); // selection automatically updated (MovingRange) return removed; } /* Comment or uncomment the selection or the current line if there is no selection. */ void KTextEditor::DocumentPrivate::comment(KTextEditor::ViewPrivate *v, uint line, uint column, int change) { // skip word wrap bug #105373 const bool skipWordWrap = wordWrap(); if (skipWordWrap) { setWordWrap(false); } bool hassel = v->selection(); int c = 0; if (hassel) { c = v->selectionRange().start().column(); } int startAttrib = 0; Kate::TextLine ln = kateTextLine(line); if (c < ln->length()) { startAttrib = ln->attribute(c); } else if (!ln->contextStack().isEmpty()) { startAttrib = highlight()->attribute(ln->contextStack().last()); } bool hasStartLineCommentMark = !(highlight()->getCommentSingleLineStart(startAttrib).isEmpty()); bool hasStartStopCommentMark = (!(highlight()->getCommentStart(startAttrib).isEmpty()) && !(highlight()->getCommentEnd(startAttrib).isEmpty())); if (change > 0) { // comment if (!hassel) { if (hasStartLineCommentMark) { addStartLineCommentToSingleLine(line, startAttrib); } else if (hasStartStopCommentMark) { addStartStopCommentToSingleLine(line, startAttrib); } } else { // anders: prefer single line comment to avoid nesting probs // If the selection starts after first char in the first line // or ends before the last char of the last line, we may use // multiline comment markers. // TODO We should try to detect nesting. // - if selection ends at col 0, most likely she wanted that // line ignored const KTextEditor::Range sel = v->selectionRange(); if (hasStartStopCommentMark && (!hasStartLineCommentMark || ( (sel.start().column() > m_buffer->plainLine(sel.start().line())->firstChar()) || (sel.end().column() > 0 && sel.end().column() < (m_buffer->plainLine(sel.end().line())->length())) ))) { addStartStopCommentToSelection(v, startAttrib); } else if (hasStartLineCommentMark) { addStartLineCommentToSelection(v, startAttrib); } } } else { // uncomment bool removed = false; if (!hassel) { removed = (hasStartLineCommentMark && removeStartLineCommentFromSingleLine(line, startAttrib)) || (hasStartStopCommentMark && removeStartStopCommentFromSingleLine(line, startAttrib)); } else { // anders: this seems like it will work with above changes :) removed = (hasStartStopCommentMark && removeStartStopCommentFromSelection(v, startAttrib)) || (hasStartLineCommentMark && removeStartLineCommentFromSelection(v, startAttrib)); } // recursive call for toggle comment if (!removed && change == 0) { comment(v, line, column, 1); } } if (skipWordWrap) { setWordWrap(true); // see begin of function ::comment (bug #105373) } } void KTextEditor::DocumentPrivate::transform(KTextEditor::ViewPrivate *v, const KTextEditor::Cursor &c, KTextEditor::DocumentPrivate::TextTransform t) { if (v->selection()) { editStart(); // cache the selection and cursor, so we can be sure to restore. KTextEditor::Range selection = v->selectionRange(); KTextEditor::Range range(selection.start(), 0); while (range.start().line() <= selection.end().line()) { int start = 0; int end = lineLength(range.start().line()); if (range.start().line() == selection.start().line() || v->blockSelection()) { start = selection.start().column(); } if (range.start().line() == selection.end().line() || v->blockSelection()) { end = selection.end().column(); } if (start > end) { int swapCol = start; start = end; end = swapCol; } range.setStart(KTextEditor::Cursor(range.start().line(), start)); range.setEnd(KTextEditor::Cursor(range.end().line(), end)); QString s = text(range); QString old = s; if (t == Uppercase) { s = s.toUpper(); } else if (t == Lowercase) { s = s.toLower(); } else { // Capitalize Kate::TextLine l = m_buffer->plainLine(range.start().line()); int p(0); while (p < s.length()) { // If bol or the character before is not in a word, up this one: // 1. if both start and p is 0, upper char. // 2. if blockselect or first line, and p == 0 and start-1 is not in a word, upper // 3. if p-1 is not in a word, upper. if ((! range.start().column() && ! p) || ((range.start().line() == selection.start().line() || v->blockSelection()) && ! p && ! highlight()->isInWord(l->at(range.start().column() - 1))) || (p && ! highlight()->isInWord(s.at(p - 1))) ) { s[p] = s.at(p).toUpper(); } p++; } } if (s != old) { removeText(range); insertText(range.start(), s); } range.setBothLines(range.start().line() + 1); } editEnd(); // restore selection & cursor v->setSelection(selection); v->setCursorPosition(c); } else { // no selection editStart(); // get cursor KTextEditor::Cursor cursor = c; QString old = text(KTextEditor::Range(cursor, 1)); QString s; switch (t) { case Uppercase: s = old.toUpper(); break; case Lowercase: s = old.toLower(); break; case Capitalize: { Kate::TextLine l = m_buffer->plainLine(cursor.line()); while (cursor.column() > 0 && highlight()->isInWord(l->at(cursor.column() - 1), l->attribute(cursor.column() - 1))) { cursor.setColumn(cursor.column() - 1); } old = text(KTextEditor::Range(cursor, 1)); s = old.toUpper(); } break; default: break; } removeText(KTextEditor::Range(cursor, 1)); insertText(cursor, s); editEnd(); } } void KTextEditor::DocumentPrivate::joinLines(uint first, uint last) { // if ( first == last ) last += 1; editStart(); int line(first); while (first < last) { // Normalize the whitespace in the joined lines by making sure there's // always exactly one space between the joined lines // This cannot be done in editUnwrapLine, because we do NOT want this // behavior when deleting from the start of a line, just when explicitly // calling the join command Kate::TextLine l = kateTextLine(line); Kate::TextLine tl = kateTextLine(line + 1); if (!l || !tl) { editEnd(); return; } int pos = tl->firstChar(); if (pos >= 0) { if (pos != 0) { editRemoveText(line + 1, 0, pos); } if (!(l->length() == 0 || l->at(l->length() - 1).isSpace())) { editInsertText(line + 1, 0, QLatin1String(" ")); } } else { // Just remove the whitespace and let Kate handle the rest editRemoveText(line + 1, 0, tl->length()); } editUnWrapLine(line); first++; } editEnd(); } void KTextEditor::DocumentPrivate::tagLines(int start, int end) { foreach (KTextEditor::ViewPrivate *view, m_views) { view->tagLines(start, end, true); } } void KTextEditor::DocumentPrivate::repaintViews(bool paintOnlyDirty) { foreach (KTextEditor::ViewPrivate *view, m_views) { view->repaintText(paintOnlyDirty); } } /* Bracket matching uses the following algorithm: If in overwrite mode, match the bracket currently underneath the cursor. Otherwise, if the character to the left is a bracket, match it. Otherwise if the character to the right of the cursor is a bracket, match it. Otherwise, don't match anything. */ KTextEditor::Range KTextEditor::DocumentPrivate::findMatchingBracket(const KTextEditor::Cursor &start, int maxLines) { if (maxLines < 0) { return KTextEditor::Range::invalid(); } Kate::TextLine textLine = m_buffer->plainLine(start.line()); if (!textLine) { return KTextEditor::Range::invalid(); } KTextEditor::Range range(start, start); const QChar right = textLine->at(range.start().column()); const QChar left = textLine->at(range.start().column() - 1); QChar bracket; if (config()->ovr()) { if (isBracket(right)) { bracket = right; } else { return KTextEditor::Range::invalid(); } } else if (isBracket(right)) { bracket = right; } else if (isBracket(left)) { range.setStart(KTextEditor::Cursor(range.start().line(), range.start().column() - 1)); bracket = left; } else { return KTextEditor::Range::invalid(); } const QChar opposite = matchingBracket(bracket, false); if (opposite.isNull()) { return KTextEditor::Range::invalid(); } const int searchDir = isStartBracket(bracket) ? 1 : -1; uint nesting = 0; const int minLine = qMax(range.start().line() - maxLines, 0); const int maxLine = qMin(range.start().line() + maxLines, documentEnd().line()); range.setEnd(range.start()); KTextEditor::DocumentCursor cursor(this); cursor.setPosition(range.start()); int validAttr = kateTextLine(cursor.line())->attribute(cursor.column()); while (cursor.line() >= minLine && cursor.line() <= maxLine) { if (!cursor.move(searchDir)) { return KTextEditor::Range::invalid(); } Kate::TextLine textLine = kateTextLine(cursor.line()); if (textLine->attribute(cursor.column()) == validAttr) { // Check for match QChar c = textLine->at(cursor.column()); if (c == opposite) { if (nesting == 0) { if (searchDir > 0) { // forward range.setEnd(cursor.toCursor()); } else { range.setStart(cursor.toCursor()); } return range; } nesting--; } else if (c == bracket) { nesting++; } } } return KTextEditor::Range::invalid(); } // helper: remove \r and \n from visible document name (bug #170876) inline static QString removeNewLines(const QString &str) { QString tmp(str); return tmp.replace(QLatin1String("\r\n"), QLatin1String(" ")) .replace(QLatin1Char('\r'), QLatin1Char(' ')) .replace(QLatin1Char('\n'), QLatin1Char(' ')); } void KTextEditor::DocumentPrivate::updateDocName() { // if the name is set, and starts with FILENAME, it should not be changed! if (! url().isEmpty() && (m_docName == removeNewLines(url().fileName()) || m_docName.startsWith(removeNewLines(url().fileName()) + QLatin1String(" (")))) { return; } int count = -1; foreach (KTextEditor::DocumentPrivate *doc, KTextEditor::EditorPrivate::self()->kateDocuments()) { if ((doc != this) && (doc->url().fileName() == url().fileName())) if (doc->m_docNameNumber > count) { count = doc->m_docNameNumber; } } m_docNameNumber = count + 1; QString oldName = m_docName; m_docName = removeNewLines(url().fileName()); m_isUntitled = m_docName.isEmpty(); if (m_isUntitled) { m_docName = i18n("Untitled"); } if (m_docNameNumber > 0) { m_docName = QString(m_docName + QLatin1String(" (%1)")).arg(m_docNameNumber + 1); } /** * avoid to emit this, if name doesn't change! */ if (oldName != m_docName) { emit documentNameChanged(this); } } void KTextEditor::DocumentPrivate::slotModifiedOnDisk(KTextEditor::View * /*v*/) { if (url().isEmpty() || !m_modOnHd) { return; } if (!m_fileChangedDialogsActivated || m_modOnHdHandler) { return; } // don't ask the user again and again the same thing if (m_modOnHdReason == m_prevModOnHdReason) { return; } m_prevModOnHdReason = m_modOnHdReason; m_modOnHdHandler = new KateModOnHdPrompt(this, m_modOnHdReason, reasonedMOHString()); connect(m_modOnHdHandler.data(), &KateModOnHdPrompt::saveAsTriggered, this, &DocumentPrivate::onModOnHdSaveAs); connect(m_modOnHdHandler.data(), &KateModOnHdPrompt::reloadTriggered, this, &DocumentPrivate::onModOnHdReload); connect(m_modOnHdHandler.data(), &KateModOnHdPrompt::ignoreTriggered, this, &DocumentPrivate::onModOnHdIgnore); } void KTextEditor::DocumentPrivate::onModOnHdSaveAs() { m_modOnHd = false; QWidget *parentWidget(dialogParent()); const QUrl res = QFileDialog::getSaveFileUrl(parentWidget, i18n("Save File"), url(), {}, nullptr, QFileDialog::DontConfirmOverwrite); if (!res.isEmpty() && checkOverwrite(res, parentWidget)) { if (! saveAs(res)) { KMessageBox::error(parentWidget, i18n("Save failed")); m_modOnHd = true; } else { delete m_modOnHdHandler; m_prevModOnHdReason = OnDiskUnmodified; emit modifiedOnDisk(this, false, OnDiskUnmodified); } } else { // the save as dialog was canceled, we are still modified on disk m_modOnHd = true; } } void KTextEditor::DocumentPrivate::onModOnHdReload() { m_modOnHd = false; m_prevModOnHdReason = OnDiskUnmodified; emit modifiedOnDisk(this, false, OnDiskUnmodified); documentReload(); delete m_modOnHdHandler; } void KTextEditor::DocumentPrivate::onModOnHdIgnore() { // ignore as long as m_prevModOnHdReason == m_modOnHdReason delete m_modOnHdHandler; } void KTextEditor::DocumentPrivate::setModifiedOnDisk(ModifiedOnDiskReason reason) { m_modOnHdReason = reason; m_modOnHd = (reason != OnDiskUnmodified); emit modifiedOnDisk(this, (reason != OnDiskUnmodified), reason); } class KateDocumentTmpMark { public: QString line; KTextEditor::Mark mark; }; void KTextEditor::DocumentPrivate::setModifiedOnDiskWarning(bool on) { m_fileChangedDialogsActivated = on; } bool KTextEditor::DocumentPrivate::documentReload() { if (url().isEmpty()) { return false; } // typically, the message for externally modified files is visible. Since it // does not make sense showing an additional dialog, just hide the message. delete m_modOnHdHandler; if (m_modOnHd && m_fileChangedDialogsActivated) { QWidget *parentWidget(dialogParent()); int i = KMessageBox::warningYesNoCancel (parentWidget, reasonedMOHString() + QLatin1String("\n\n") + i18n("What do you want to do?"), i18n("File Was Changed on Disk"), KGuiItem(i18n("&Reload File"), QStringLiteral("view-refresh")), KGuiItem(i18n("&Ignore Changes"), QStringLiteral("dialog-warning"))); if (i != KMessageBox::Yes) { if (i == KMessageBox::No) { m_modOnHd = false; m_modOnHdReason = OnDiskUnmodified; m_prevModOnHdReason = OnDiskUnmodified; emit modifiedOnDisk(this, m_modOnHd, m_modOnHdReason); } // reset some flags only valid for one reload! m_userSetEncodingForNextReload = false; return false; } } emit aboutToReload(this); QList tmp; for (QHash::const_iterator i = m_marks.constBegin(); i != m_marks.constEnd(); ++i) { KateDocumentTmpMark m; m.line = line(i.value()->line); m.mark = *i.value(); tmp.append(m); } const QString oldMode = mode(); const bool byUser = m_fileTypeSetByUser; const QString hl_mode = highlightingMode(); m_storedVariables.clear(); // save cursor positions for all views QHash cursorPositions; for (auto it = m_views.constBegin(); it != m_views.constEnd(); ++it) { auto v = it.value(); cursorPositions.insert(v, v->cursorPosition()); } m_reloading = true; KTextEditor::DocumentPrivate::openUrl(url()); // reset some flags only valid for one reload! m_userSetEncodingForNextReload = false; // restore cursor positions for all views for (auto it = m_views.constBegin(); it != m_views.constEnd(); ++it) { auto v = it.value(); setActiveView(v); v->setCursorPosition(cursorPositions.value(v)); if (v->isVisible()) { v->repaintText(false); } } for (int z = 0; z < tmp.size(); z++) { - if (z < (int)lines()) { + if (z < lines()) { if (line(tmp.at(z).mark.line) == tmp.at(z).line) { setMark(tmp.at(z).mark.line, tmp.at(z).mark.type); } } } if (byUser) { setMode(oldMode); } setHighlightingMode(hl_mode); emit reloaded(this); return true; } bool KTextEditor::DocumentPrivate::documentSave() { if (!url().isValid() || !isReadWrite()) { return documentSaveAs(); } return save(); } bool KTextEditor::DocumentPrivate::documentSaveAs() { const QUrl saveUrl = QFileDialog::getSaveFileUrl(dialogParent(), i18n("Save File"), url(), {}, nullptr, QFileDialog::DontConfirmOverwrite); if (saveUrl.isEmpty() || !checkOverwrite(saveUrl, dialogParent())) { return false; } return saveAs(saveUrl); } bool KTextEditor::DocumentPrivate::documentSaveAsWithEncoding(const QString &encoding) { const QUrl saveUrl = QFileDialog::getSaveFileUrl(dialogParent(), i18n("Save File"), url(), {}, nullptr, QFileDialog::DontConfirmOverwrite); if (saveUrl.isEmpty() || !checkOverwrite(saveUrl, dialogParent())) { return false; } setEncoding(encoding); return saveAs(saveUrl); } bool KTextEditor::DocumentPrivate::documentSaveCopyAs() { const QUrl saveUrl = QFileDialog::getSaveFileUrl(dialogParent(), i18n("Save Copy of File"), url(), {}, nullptr, QFileDialog::DontConfirmOverwrite); if (saveUrl.isEmpty() || !checkOverwrite(saveUrl, dialogParent())) { return false; } QTemporaryFile file; if (!file.open()) { return false; } if (!m_buffer->saveFile(file.fileName())) { KMessageBox::error(dialogParent(), i18n("The document could not be saved, as it was not possible to write to %1.\n\nCheck that you have write access to this file or that enough disk space is available.", this->url().toDisplayString(QUrl::PreferLocalFile))); return false; } // get the right permissions, start with safe default KIO::StatJob *statJob = KIO::stat(url(), KIO::StatJob::SourceSide, 2); KJobWidgets::setWindow(statJob, QApplication::activeWindow()); int permissions = -1; if (statJob->exec()) { permissions = KFileItem(statJob->statResult(), url()).permissions(); } // KIO move, important: allow overwrite, we checked above! KIO::FileCopyJob *job = KIO::file_copy(QUrl::fromLocalFile(file.fileName()), saveUrl, permissions, KIO::Overwrite); KJobWidgets::setWindow(job, QApplication::activeWindow()); return job->exec(); } void KTextEditor::DocumentPrivate::setWordWrap(bool on) { config()->setWordWrap(on); } bool KTextEditor::DocumentPrivate::wordWrap() const { return config()->wordWrap(); } void KTextEditor::DocumentPrivate::setWordWrapAt(uint col) { config()->setWordWrapAt(col); } unsigned int KTextEditor::DocumentPrivate::wordWrapAt() const { return config()->wordWrapAt(); } void KTextEditor::DocumentPrivate::setPageUpDownMovesCursor(bool on) { config()->setPageUpDownMovesCursor(on); } bool KTextEditor::DocumentPrivate::pageUpDownMovesCursor() const { return config()->pageUpDownMovesCursor(); } //END bool KTextEditor::DocumentPrivate::setEncoding(const QString &e) { return m_config->setEncoding(e); } QString KTextEditor::DocumentPrivate::encoding() const { return m_config->encoding(); } void KTextEditor::DocumentPrivate::updateConfig() { m_undoManager->updateConfig(); // switch indenter if needed and update config.... m_indenter->setMode(m_config->indentationMode()); m_indenter->updateConfig(); // set tab width there, too m_buffer->setTabWidth(config()->tabWidth()); // update all views, does tagAll and updateView... foreach (KTextEditor::ViewPrivate *view, m_views) { view->updateDocumentConfig(); } // update on-the-fly spell checking as spell checking defaults might have changes if (m_onTheFlyChecker) { m_onTheFlyChecker->updateConfig(); } emit configChanged(); } //BEGIN Variable reader // "local variable" feature by anders, 2003 /* TODO add config options (how many lines to read, on/off) add interface for plugins/apps to set/get variables add view stuff */ void KTextEditor::DocumentPrivate::readVariables(bool onlyViewAndRenderer) { if (!onlyViewAndRenderer) { m_config->configStart(); } // views! KTextEditor::ViewPrivate *v; foreach (v, m_views) { v->config()->configStart(); v->renderer()->config()->configStart(); } // read a number of lines in the top/bottom of the document for (int i = 0; i < qMin(9, lines()); ++i) { readVariableLine(line(i), onlyViewAndRenderer); } if (lines() > 10) { for (int i = qMax(10, lines() - 10); i < lines(); i++) { readVariableLine(line(i), onlyViewAndRenderer); } } if (!onlyViewAndRenderer) { m_config->configEnd(); } foreach (v, m_views) { v->config()->configEnd(); v->renderer()->config()->configEnd(); } } void KTextEditor::DocumentPrivate::readVariableLine(QString t, bool onlyViewAndRenderer) { static const QRegularExpression kvLine(QStringLiteral("kate:(.*)")); static const QRegularExpression kvLineWildcard(QStringLiteral("kate-wildcard\\((.*)\\):(.*)")); static const QRegularExpression kvLineMime(QStringLiteral("kate-mimetype\\((.*)\\):(.*)")); static const QRegularExpression kvVar(QStringLiteral("([\\w\\-]+)\\s+([^;]+)")); // simple check first, no regex // no kate inside, no vars, simple... if (!t.contains(QLatin1String("kate"))) { return; } // found vars, if any QString s; // now, try first the normal ones auto match = kvLine.match(t); if (match.hasMatch()) { s = match.captured(1); //qCDebug(LOG_KTE) << "normal variable line kate: matched: " << s; } else if ((match = kvLineWildcard.match(t)).hasMatch()) { // regex given const QStringList wildcards(match.captured(1).split(QLatin1Char(';'), QString::SkipEmptyParts)); const QString nameOfFile = url().fileName(); bool found = false; foreach (const QString &pattern, wildcards) { QRegExp wildcard(pattern, Qt::CaseSensitive, QRegExp::Wildcard); found = wildcard.exactMatch(nameOfFile); if (found) { break; } } // nothing usable found... if (!found) { return; } s = match.captured(2); //qCDebug(LOG_KTE) << "guarded variable line kate-wildcard: matched: " << s; } else if ((match = kvLineMime.match(t)).hasMatch()) { // mime-type given const QStringList types(match.captured(1).split(QLatin1Char(';'), QString::SkipEmptyParts)); // no matching type found if (!types.contains(mimeType())) { return; } s = match.captured(2); //qCDebug(LOG_KTE) << "guarded variable line kate-mimetype: matched: " << s; } else { // nothing found return; } // view variable names static const auto vvl = { QLatin1String("dynamic-word-wrap") , QLatin1String("dynamic-word-wrap-indicators") , QLatin1String("line-numbers") , QLatin1String("icon-border") , QLatin1String("folding-markers") , QLatin1String("folding-preview") , QLatin1String("bookmark-sorting") , QLatin1String("auto-center-lines") , QLatin1String("icon-bar-color") , QLatin1String("scrollbar-minimap") , QLatin1String("scrollbar-preview") // renderer , QLatin1String("background-color") , QLatin1String("selection-color") , QLatin1String("current-line-color") , QLatin1String("bracket-highlight-color") , QLatin1String("word-wrap-marker-color") , QLatin1String("font") , QLatin1String("font-size") , QLatin1String("scheme") }; int spaceIndent = -1; // for backward compatibility; see below bool replaceTabsSet = false; int startPos(0); QString var, val; while ((match = kvVar.match(s, startPos)).hasMatch()) { startPos = match.capturedEnd(0); var = match.captured(1); val = match.captured(2).trimmed(); bool state; // store booleans here int n; // store ints here // only apply view & renderer config stuff if (onlyViewAndRenderer) { if (contains(vvl, var)) { // FIXME define above setViewVariable(var, val); } } else { // BOOL SETTINGS if (var == QLatin1String("word-wrap") && checkBoolValue(val, &state)) { setWordWrap(state); // ??? FIXME CHECK } // KateConfig::configFlags // FIXME should this be optimized to only a few calls? how? else if (var == QLatin1String("backspace-indents") && checkBoolValue(val, &state)) { m_config->setBackspaceIndents(state); } else if (var == QLatin1String("indent-pasted-text") && checkBoolValue(val, &state)) { m_config->setIndentPastedText(state); } else if (var == QLatin1String("replace-tabs") && checkBoolValue(val, &state)) { m_config->setReplaceTabsDyn(state); replaceTabsSet = true; // for backward compatibility; see below } else if (var == QLatin1String("remove-trailing-space") && checkBoolValue(val, &state)) { qCWarning(LOG_KTE) << i18n("Using deprecated modeline 'remove-trailing-space'. " "Please replace with 'remove-trailing-spaces modified;', see " "http://docs.kde.org/stable/en/applications/kate/config-variables.html#variable-remove-trailing-spaces"); m_config->setRemoveSpaces(state ? 1 : 0); } else if (var == QLatin1String("replace-trailing-space-save") && checkBoolValue(val, &state)) { qCWarning(LOG_KTE) << i18n("Using deprecated modeline 'replace-trailing-space-save'. " "Please replace with 'remove-trailing-spaces all;', see " "http://docs.kde.org/stable/en/applications/kate/config-variables.html#variable-remove-trailing-spaces"); m_config->setRemoveSpaces(state ? 2 : 0); } else if (var == QLatin1String("overwrite-mode") && checkBoolValue(val, &state)) { m_config->setOvr(state); } else if (var == QLatin1String("keep-extra-spaces") && checkBoolValue(val, &state)) { m_config->setKeepExtraSpaces(state); } else if (var == QLatin1String("tab-indents") && checkBoolValue(val, &state)) { m_config->setTabIndents(state); } else if (var == QLatin1String("show-tabs") && checkBoolValue(val, &state)) { m_config->setShowTabs(state); } else if (var == QLatin1String("show-trailing-spaces") && checkBoolValue(val, &state)) { m_config->setShowSpaces(state); } else if (var == QLatin1String("space-indent") && checkBoolValue(val, &state)) { // this is for backward compatibility; see below spaceIndent = state; } else if (var == QLatin1String("smart-home") && checkBoolValue(val, &state)) { m_config->setSmartHome(state); } else if (var == QLatin1String("newline-at-eof") && checkBoolValue(val, &state)) { m_config->setNewLineAtEof(state); } // INTEGER SETTINGS else if (var == QLatin1String("tab-width") && checkIntValue(val, &n)) { m_config->setTabWidth(n); } else if (var == QLatin1String("indent-width") && checkIntValue(val, &n)) { m_config->setIndentationWidth(n); } else if (var == QLatin1String("indent-mode")) { m_config->setIndentationMode(val); } else if (var == QLatin1String("word-wrap-column") && checkIntValue(val, &n) && n > 0) { // uint, but hard word wrap at 0 will be no fun ;) m_config->setWordWrapAt(n); } // STRING SETTINGS else if (var == QLatin1String("eol") || var == QLatin1String("end-of-line")) { const auto l = { QLatin1String("unix"), QLatin1String("dos"), QLatin1String("mac") }; if ((n = indexOf(l, val.toLower())) != -1) { /** * set eol + avoid that it is overwritten by auto-detection again! * this fixes e.g. .kateconfig files with // kate: eol dos; to work, bug 365705 */ m_config->setEol(n); m_config->setAllowEolDetection(false); } } else if (var == QLatin1String("bom") || var == QLatin1String("byte-order-mark") || var == QLatin1String("byte-order-marker")) { if (checkBoolValue(val, &state)) { m_config->setBom(state); } } else if (var == QLatin1String("remove-trailing-spaces")) { val = val.toLower(); if (val == QLatin1String("1") || val == QLatin1String("modified") || val == QLatin1String("mod") || val == QLatin1String("+")) { m_config->setRemoveSpaces(1); } else if (val == QLatin1String("2") || val == QLatin1String("all") || val == QLatin1String("*")) { m_config->setRemoveSpaces(2); } else { m_config->setRemoveSpaces(0); } } else if (var == QLatin1String("syntax") || var == QLatin1String("hl")) { setHighlightingMode(val); } else if (var == QLatin1String("mode")) { setMode(val); } else if (var == QLatin1String("encoding")) { setEncoding(val); } else if (var == QLatin1String("default-dictionary")) { setDefaultDictionary(val); } else if (var == QLatin1String("automatic-spell-checking") && checkBoolValue(val, &state)) { onTheFlySpellCheckingEnabled(state); } // VIEW SETTINGS else if (contains(vvl, var)) { setViewVariable(var, val); } else { m_storedVariables.insert(var, val); } } } // Backward compatibility // If space-indent was set, but replace-tabs was not set, we assume // that the user wants to replace tabulators and set that flag. // If both were set, replace-tabs has precedence. // At this point spaceIndent is -1 if it was never set, // 0 if it was set to off, and 1 if it was set to on. // Note that if onlyViewAndRenderer was requested, spaceIndent is -1. if (!replaceTabsSet && spaceIndent >= 0) { m_config->setReplaceTabsDyn(spaceIndent > 0); } } void KTextEditor::DocumentPrivate::setViewVariable(QString var, QString val) { KTextEditor::ViewPrivate *v; bool state; int n; QColor c; foreach (v, m_views) { if (var == QLatin1String("auto-brackets") && checkBoolValue(val, &state)) { v->config()->setAutoBrackets(state); } else if (var == QLatin1String("dynamic-word-wrap") && checkBoolValue(val, &state)) { v->config()->setDynWordWrap(state); } else if (var == QLatin1String("persistent-selection") && checkBoolValue(val, &state)) { v->config()->setPersistentSelection(state); } else if (var == QLatin1String("block-selection") && checkBoolValue(val, &state)) { v->setBlockSelection(state); } //else if ( var = "dynamic-word-wrap-indicators" ) else if (var == QLatin1String("line-numbers") && checkBoolValue(val, &state)) { v->config()->setLineNumbers(state); } else if (var == QLatin1String("icon-border") && checkBoolValue(val, &state)) { v->config()->setIconBar(state); } else if (var == QLatin1String("folding-markers") && checkBoolValue(val, &state)) { v->config()->setFoldingBar(state); } else if (var == QLatin1String("folding-preview") && checkBoolValue(val, &state)) { v->config()->setFoldingPreview(state); } else if (var == QLatin1String("auto-center-lines") && checkIntValue(val, &n)) { v->config()->setAutoCenterLines(n); } else if (var == QLatin1String("icon-bar-color") && checkColorValue(val, c)) { v->renderer()->config()->setIconBarColor(c); } else if (var == QLatin1String("scrollbar-minimap") && checkBoolValue(val, &state)) { v->config()->setScrollBarMiniMap(state); } else if (var == QLatin1String("scrollbar-preview") && checkBoolValue(val, &state)) { v->config()->setScrollBarPreview(state); } // RENDERER else if (var == QLatin1String("background-color") && checkColorValue(val, c)) { v->renderer()->config()->setBackgroundColor(c); } else if (var == QLatin1String("selection-color") && checkColorValue(val, c)) { v->renderer()->config()->setSelectionColor(c); } else if (var == QLatin1String("current-line-color") && checkColorValue(val, c)) { v->renderer()->config()->setHighlightedLineColor(c); } else if (var == QLatin1String("bracket-highlight-color") && checkColorValue(val, c)) { v->renderer()->config()->setHighlightedBracketColor(c); } else if (var == QLatin1String("word-wrap-marker-color") && checkColorValue(val, c)) { v->renderer()->config()->setWordWrapMarkerColor(c); } else if (var == QLatin1String("font") || (checkIntValue(val, &n) && var == QLatin1String("font-size"))) { QFont _f(v->renderer()->config()->font()); if (var == QLatin1String("font")) { _f.setFamily(val); _f.setFixedPitch(QFont(val).fixedPitch()); } else { _f.setPointSize(n); } v->renderer()->config()->setFont(_f); } else if (var == QLatin1String("scheme")) { v->renderer()->config()->setSchema(val); } } } bool KTextEditor::DocumentPrivate::checkBoolValue(QString val, bool *result) { val = val.trimmed().toLower(); static const auto trueValues = { QLatin1String("1"), QLatin1String("on"), QLatin1String("true") }; if (contains(trueValues, val)) { *result = true; return true; } static const auto falseValues = { QLatin1String("0"), QLatin1String("off"), QLatin1String("false") }; if (contains(falseValues, val)) { *result = false; return true; } return false; } bool KTextEditor::DocumentPrivate::checkIntValue(QString val, int *result) { bool ret(false); *result = val.toInt(&ret); return ret; } bool KTextEditor::DocumentPrivate::checkColorValue(QString val, QColor &c) { c.setNamedColor(val); return c.isValid(); } // KTextEditor::variable QString KTextEditor::DocumentPrivate::variable(const QString &name) const { return m_storedVariables.value(name, QString()); } void KTextEditor::DocumentPrivate::setVariable(const QString &name, const QString &value) { QString s = QStringLiteral("kate: "); s.append(name); s.append(QLatin1Char(' ')); s.append(value); readVariableLine(s); } //END void KTextEditor::DocumentPrivate::slotModOnHdDirty(const QString &path) { if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskModified)) { m_modOnHd = true; m_modOnHdReason = OnDiskModified; if (!m_modOnHdTimer.isActive()) { m_modOnHdTimer.start(); } } } void KTextEditor::DocumentPrivate::slotModOnHdCreated(const QString &path) { if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskCreated)) { m_modOnHd = true; m_modOnHdReason = OnDiskCreated; if (!m_modOnHdTimer.isActive()) { m_modOnHdTimer.start(); } } } void KTextEditor::DocumentPrivate::slotModOnHdDeleted(const QString &path) { if ((path == m_dirWatchFile) && (!m_modOnHd || m_modOnHdReason != OnDiskDeleted)) { m_modOnHd = true; m_modOnHdReason = OnDiskDeleted; if (!m_modOnHdTimer.isActive()) { m_modOnHdTimer.start(); } } } void KTextEditor::DocumentPrivate::slotDelayedHandleModOnHd() { // compare git hash with the one we have (if we have one) const QByteArray oldDigest = checksum(); if (!oldDigest.isEmpty() && !url().isEmpty() && url().isLocalFile()) { /** * if current checksum == checksum of new file => unmodified */ if (m_modOnHdReason != OnDiskDeleted && createDigest() && oldDigest == checksum()) { m_modOnHd = false; m_modOnHdReason = OnDiskUnmodified; m_prevModOnHdReason = OnDiskUnmodified; } #if LIBGIT2_FOUND /** * if still modified, try to take a look at git * skip that, if document is modified! * only do that, if the file is still there, else reload makes no sense! */ if (m_modOnHd && !isModified() && QFile::exists(url().toLocalFile())) { /** * try to discover the git repo of this file * libgit2 docs state that UTF-8 is the right encoding, even on windows * I hope that is correct! */ git_repository *repository = nullptr; const QByteArray utf8Path = url().toLocalFile().toUtf8(); if (git_repository_open_ext(&repository, utf8Path.constData(), 0, nullptr) == 0) { /** * if we have repo, convert the git hash to an OID */ git_oid oid; if (git_oid_fromstr(&oid, oldDigest.toHex().data()) == 0) { /** * finally: is there a blob for this git hash? */ git_blob *blob = nullptr; if (git_blob_lookup(&blob, repository, &oid) == 0) { /** * this hash exists still in git => just reload */ m_modOnHd = false; m_modOnHdReason = OnDiskUnmodified; m_prevModOnHdReason = OnDiskUnmodified; documentReload(); } git_blob_free(blob); } } git_repository_free(repository); } #endif } /** * emit our signal to the outside! */ emit modifiedOnDisk(this, m_modOnHd, m_modOnHdReason); } QByteArray KTextEditor::DocumentPrivate::checksum() const { return m_buffer->digest(); } bool KTextEditor::DocumentPrivate::createDigest() { QByteArray digest; if (url().isLocalFile()) { QFile f(url().toLocalFile()); if (f.open(QIODevice::ReadOnly)) { // init the hash with the git header QCryptographicHash crypto(QCryptographicHash::Sha1); const QString header = QStringLiteral("blob %1").arg(f.size()); crypto.addData(header.toLatin1() + '\0'); while (!f.atEnd()) { crypto.addData(f.read(256 * 1024)); } digest = crypto.result(); } } /** * set new digest */ m_buffer->setDigest(digest); return !digest.isEmpty(); } QString KTextEditor::DocumentPrivate::reasonedMOHString() const { // squeeze path const QString str = KStringHandler::csqueeze(url().toDisplayString(QUrl::PreferLocalFile)); switch (m_modOnHdReason) { case OnDiskModified: return i18n("The file '%1' was modified by another program.", str); break; case OnDiskCreated: return i18n("The file '%1' was created by another program.", str); break; case OnDiskDeleted: return i18n("The file '%1' was deleted by another program.", str); break; default: return QString(); } Q_UNREACHABLE(); return QString(); } void KTextEditor::DocumentPrivate::removeTrailingSpaces() { const int remove = config()->removeSpaces(); if (remove == 0) { return; } // temporarily disable static word wrap (see bug #328900) const bool wordWrapEnabled = config()->wordWrap(); if (wordWrapEnabled) { setWordWrap(false); } editStart(); for (int line = 0; line < lines(); ++line) { Kate::TextLine textline = plainKateTextLine(line); // remove trailing spaces in entire document, remove = 2 // remove trailing spaces of touched lines, remove = 1 // remove trailing spaces of lines saved on disk, remove = 1 if (remove == 2 || textline->markedAsModified() || textline->markedAsSavedOnDisk()) { const int p = textline->lastChar() + 1; const int l = textline->length() - p; if (l > 0) { editRemoveText(line, p, l); } } } editEnd(); // enable word wrap again, if it was enabled (see bug #328900) if (wordWrapEnabled) { setWordWrap(true); // see begin of this function } } void KTextEditor::DocumentPrivate::updateFileType(const QString &newType, bool user) { if (user || !m_fileTypeSetByUser) { if (!newType.isEmpty()) { // remember that we got set by user m_fileTypeSetByUser = user; m_fileType = newType; m_config->configStart(); if (!m_hlSetByUser && !KTextEditor::EditorPrivate::self()->modeManager()->fileType(newType).hl.isEmpty()) { int hl(KateHlManager::self()->nameFind(KTextEditor::EditorPrivate::self()->modeManager()->fileType(newType).hl)); if (hl >= 0) { m_buffer->setHighlight(hl); } } /** * set the indentation mode, if any in the mode... * and user did not set it before! */ if (!m_indenterSetByUser && !KTextEditor::EditorPrivate::self()->modeManager()->fileType(newType).indenter.isEmpty()) { config()->setIndentationMode(KTextEditor::EditorPrivate::self()->modeManager()->fileType(newType).indenter); } // views! KTextEditor::ViewPrivate *v; foreach (v, m_views) { v->config()->configStart(); v->renderer()->config()->configStart(); } bool bom_settings = false; if (m_bomSetByUser) { bom_settings = m_config->bom(); } readVariableLine(KTextEditor::EditorPrivate::self()->modeManager()->fileType(newType).varLine); if (m_bomSetByUser) { m_config->setBom(bom_settings); } m_config->configEnd(); foreach (v, m_views) { v->config()->configEnd(); v->renderer()->config()->configEnd(); } } } // fixme, make this better... emit modeChanged(this); } void KTextEditor::DocumentPrivate::slotQueryClose_save(bool *handled, bool *abortClosing) { *handled = true; *abortClosing = true; if (this->url().isEmpty()) { QWidget *parentWidget(dialogParent()); const QUrl res = QFileDialog::getSaveFileUrl(parentWidget, i18n("Save File"), QUrl(), {}, nullptr, QFileDialog::DontConfirmOverwrite); if (res.isEmpty() || !checkOverwrite(res, parentWidget)) { *abortClosing = true; return; } saveAs(res); *abortClosing = false; } else { save(); *abortClosing = false; } } bool KTextEditor::DocumentPrivate::checkOverwrite(QUrl u, QWidget *parent) { if (!u.isLocalFile()) { return true; } QFileInfo info(u.path()); if (!info.exists()) { return true; } return KMessageBox::Cancel != KMessageBox::warningContinueCancel(parent, i18n("A file named \"%1\" already exists. " "Are you sure you want to overwrite it?", info.fileName()), i18n("Overwrite File?"), KStandardGuiItem::overwrite(), KStandardGuiItem::cancel(), QString(), KMessageBox::Options(KMessageBox::Notify | KMessageBox::Dangerous)); } //BEGIN KTextEditor::ConfigInterface // BEGIN ConfigInterface stff QStringList KTextEditor::DocumentPrivate::configKeys() const { static const QStringList keys = { QLatin1String("backup-on-save-local"), QLatin1String("backup-on-save-suffix"), QLatin1String("backup-on-save-prefix"), QLatin1String("replace-tabs"), QLatin1String("indent-pasted-text"), QLatin1String("tab-width"), QLatin1String("indent-width"), QLatin1String("on-the-fly-spellcheck"), }; return keys; } QVariant KTextEditor::DocumentPrivate::configValue(const QString &key) { if (key == QLatin1String("backup-on-save-local")) { return m_config->backupFlags() & KateDocumentConfig::LocalFiles; } else if (key == QLatin1String("backup-on-save-remote")) { return m_config->backupFlags() & KateDocumentConfig::RemoteFiles; } else if (key == QLatin1String("backup-on-save-suffix")) { return m_config->backupSuffix(); } else if (key == QLatin1String("backup-on-save-prefix")) { return m_config->backupPrefix(); } else if (key == QLatin1String("replace-tabs")) { return m_config->replaceTabsDyn(); } else if (key == QLatin1String("indent-pasted-text")) { return m_config->indentPastedText(); } else if (key == QLatin1String("tab-width")) { return m_config->tabWidth(); } else if (key == QLatin1String("indent-width")) { return m_config->indentationWidth(); } else if (key == QLatin1String("on-the-fly-spellcheck")) { return isOnTheFlySpellCheckingEnabled(); } // return invalid variant return QVariant(); } void KTextEditor::DocumentPrivate::setConfigValue(const QString &key, const QVariant &value) { if (value.type() == QVariant::String) { if (key == QLatin1String("backup-on-save-suffix")) { m_config->setBackupSuffix(value.toString()); } else if (key == QLatin1String("backup-on-save-prefix")) { m_config->setBackupPrefix(value.toString()); } } else if (value.type() == QVariant::Bool) { const bool bValue = value.toBool(); if (key == QLatin1String("backup-on-save-local")) { uint f = m_config->backupFlags(); if (bValue) { f |= KateDocumentConfig::LocalFiles; } else { f ^= KateDocumentConfig::LocalFiles; } m_config->setBackupFlags(f); } else if (key == QLatin1String("backup-on-save-remote")) { uint f = m_config->backupFlags(); if (bValue) { f |= KateDocumentConfig::RemoteFiles; } else { f ^= KateDocumentConfig::RemoteFiles; } m_config->setBackupFlags(f); } else if (key == QLatin1String("replace-tabs")) { m_config->setReplaceTabsDyn(bValue); } else if (key == QLatin1String("indent-pasted-text")) { m_config->setIndentPastedText(bValue); } else if (key == QLatin1String("on-the-fly-spellcheck")) { onTheFlySpellCheckingEnabled(bValue); } } else if (value.canConvert(QVariant::Int)) { if (key == QLatin1String("tab-width")) { config()->setTabWidth(value.toInt()); } else if (key == QLatin1String("indent-width")) { config()->setIndentationWidth(value.toInt()); } } } //END KTextEditor::ConfigInterface KTextEditor::Cursor KTextEditor::DocumentPrivate::documentEnd() const { return KTextEditor::Cursor(lastLine(), lineLength(lastLine())); } bool KTextEditor::DocumentPrivate::replaceText(const KTextEditor::Range &range, const QString &s, bool block) { // TODO more efficient? editStart(); bool changed = removeText(range, block); changed |= insertText(range.start(), s, block); editEnd(); return changed; } KateHighlighting *KTextEditor::DocumentPrivate::highlight() const { return m_buffer->highlight(); } Kate::TextLine KTextEditor::DocumentPrivate::kateTextLine(int i) { m_buffer->ensureHighlighted(i); return m_buffer->plainLine(i); } Kate::TextLine KTextEditor::DocumentPrivate::plainKateTextLine(int i) { return m_buffer->plainLine(i); } bool KTextEditor::DocumentPrivate::isEditRunning() const { return editIsRunning; } void KTextEditor::DocumentPrivate::setUndoMergeAllEdits(bool merge) { if (merge && m_undoMergeAllEdits) { // Don't add another undo safe point: it will override our current one, // meaning we'll need two undo's to get back there - which defeats the object! return; } m_undoManager->undoSafePoint(); m_undoManager->setAllowComplexMerge(merge); m_undoMergeAllEdits = merge; } //BEGIN KTextEditor::MovingInterface KTextEditor::MovingCursor *KTextEditor::DocumentPrivate::newMovingCursor(const KTextEditor::Cursor &position, KTextEditor::MovingCursor::InsertBehavior insertBehavior) { return new Kate::TextCursor(buffer(), position, insertBehavior); } KTextEditor::MovingRange *KTextEditor::DocumentPrivate::newMovingRange(const KTextEditor::Range &range, KTextEditor::MovingRange::InsertBehaviors insertBehaviors, KTextEditor::MovingRange::EmptyBehavior emptyBehavior) { return new Kate::TextRange(buffer(), range, insertBehaviors, emptyBehavior); } qint64 KTextEditor::DocumentPrivate::revision() const { return m_buffer->history().revision(); } qint64 KTextEditor::DocumentPrivate::lastSavedRevision() const { return m_buffer->history().lastSavedRevision(); } void KTextEditor::DocumentPrivate::lockRevision(qint64 revision) { m_buffer->history().lockRevision(revision); } void KTextEditor::DocumentPrivate::unlockRevision(qint64 revision) { m_buffer->history().unlockRevision(revision); } void KTextEditor::DocumentPrivate::transformCursor(int &line, int &column, KTextEditor::MovingCursor::InsertBehavior insertBehavior, qint64 fromRevision, qint64 toRevision) { m_buffer->history().transformCursor(line, column, insertBehavior, fromRevision, toRevision); } void KTextEditor::DocumentPrivate::transformCursor(KTextEditor::Cursor &cursor, KTextEditor::MovingCursor::InsertBehavior insertBehavior, qint64 fromRevision, qint64 toRevision) { int line = cursor.line(), column = cursor.column(); m_buffer->history().transformCursor(line, column, insertBehavior, fromRevision, toRevision); cursor.setLine(line); cursor.setColumn(column); } void KTextEditor::DocumentPrivate::transformRange(KTextEditor::Range &range, KTextEditor::MovingRange::InsertBehaviors insertBehaviors, KTextEditor::MovingRange::EmptyBehavior emptyBehavior, qint64 fromRevision, qint64 toRevision) { m_buffer->history().transformRange(range, insertBehaviors, emptyBehavior, fromRevision, toRevision); } //END //BEGIN KTextEditor::AnnotationInterface void KTextEditor::DocumentPrivate::setAnnotationModel(KTextEditor::AnnotationModel *model) { KTextEditor::AnnotationModel *oldmodel = m_annotationModel; m_annotationModel = model; emit annotationModelChanged(oldmodel, m_annotationModel); } KTextEditor::AnnotationModel *KTextEditor::DocumentPrivate::annotationModel() const { return m_annotationModel; } //END KTextEditor::AnnotationInterface //TAKEN FROM kparts.h bool KTextEditor::DocumentPrivate::queryClose() { if (!isReadWrite() || !isModified()) { return true; } QString docName = documentName(); int res = KMessageBox::warningYesNoCancel(dialogParent(), i18n("The document \"%1\" has been modified.\n" "Do you want to save your changes or discard them?", docName), i18n("Close Document"), KStandardGuiItem::save(), KStandardGuiItem::discard()); bool abortClose = false; bool handled = false; switch (res) { case KMessageBox::Yes : sigQueryClose(&handled, &abortClose); if (!handled) { if (url().isEmpty()) { QUrl url = QFileDialog::getSaveFileUrl(dialogParent()); if (url.isEmpty()) { return false; } saveAs(url); } else { save(); } } else if (abortClose) { return false; } return waitSaveComplete(); case KMessageBox::No : return true; default : // case KMessageBox::Cancel : return false; } } void KTextEditor::DocumentPrivate::slotStarted(KIO::Job *job) { /** * if we are idle before, we are now loading! */ if (m_documentState == DocumentIdle) { m_documentState = DocumentLoading; } /** * if loading: * - remember pre loading read-write mode * if remote load: * - set to read-only * - trigger possible message */ if (m_documentState == DocumentLoading) { /** * remember state */ m_readWriteStateBeforeLoading = isReadWrite(); /** * perhaps show loading message, but wait one second */ if (job) { /** * only read only if really remote file! */ setReadWrite(false); /** * perhaps some message about loading in one second! * remember job pointer, we want to be able to kill it! */ m_loadingJob = job; QTimer::singleShot(1000, this, SLOT(slotTriggerLoadingMessage())); } } } void KTextEditor::DocumentPrivate::slotCompleted() { /** * if were loading, reset back to old read-write mode before loading * and kill the possible loading message */ if (m_documentState == DocumentLoading) { setReadWrite(m_readWriteStateBeforeLoading); delete m_loadingMessage; } /** * Emit signal that we saved the document, if needed */ if (m_documentState == DocumentSaving || m_documentState == DocumentSavingAs) { emit documentSavedOrUploaded(this, m_documentState == DocumentSavingAs); } /** * back to idle mode */ m_documentState = DocumentIdle; m_reloading = false; } void KTextEditor::DocumentPrivate::slotCanceled() { /** * if were loading, reset back to old read-write mode before loading * and kill the possible loading message */ if (m_documentState == DocumentLoading) { setReadWrite(m_readWriteStateBeforeLoading); delete m_loadingMessage; showAndSetOpeningErrorAccess(); updateDocName(); } /** * back to idle mode */ m_documentState = DocumentIdle; m_reloading = false; } void KTextEditor::DocumentPrivate::slotTriggerLoadingMessage() { /** * no longer loading? * no message needed! */ if (m_documentState != DocumentLoading) { return; } /** * create message about file loading in progress */ delete m_loadingMessage; m_loadingMessage = new KTextEditor::Message(i18n("The file %2 is still loading.", url().toDisplayString(QUrl::PreferLocalFile), url().fileName())); m_loadingMessage->setPosition(KTextEditor::Message::TopInView); /** * if around job: add cancel action */ if (m_loadingJob) { QAction *cancel = new QAction(i18n("&Abort Loading"), nullptr); connect(cancel, SIGNAL(triggered()), this, SLOT(slotAbortLoading())); m_loadingMessage->addAction(cancel); } /** * really post message */ postMessage(m_loadingMessage); } void KTextEditor::DocumentPrivate::slotAbortLoading() { /** * no job, no work */ if (!m_loadingJob) { return; } /** * abort loading if any job * signal results! */ m_loadingJob->kill(KJob::EmitResult); m_loadingJob = nullptr; } void KTextEditor::DocumentPrivate::slotUrlChanged(const QUrl &url) { if (m_reloading) { // the URL is temporarily unset and then reset to the previous URL during reload // we do not want to notify the outside about this return; } Q_UNUSED(url); updateDocName(); emit documentUrlChanged(this); } bool KTextEditor::DocumentPrivate::save() { /** * no double save/load * we need to allow DocumentPreSavingAs here as state, as save is called in saveAs! */ if ((m_documentState != DocumentIdle) && (m_documentState != DocumentPreSavingAs)) { return false; } /** * if we are idle, we are now saving */ if (m_documentState == DocumentIdle) { m_documentState = DocumentSaving; } else { m_documentState = DocumentSavingAs; } /** * call back implementation for real work */ return KTextEditor::Document::save(); } bool KTextEditor::DocumentPrivate::saveAs(const QUrl &url) { /** * abort on bad URL * that is done in saveAs below, too * but we must check it here already to avoid messing up * as no signals will be send, then */ if (!url.isValid()) { return false; } /** * no double save/load */ if (m_documentState != DocumentIdle) { return false; } /** * we enter the pre save as phase */ m_documentState = DocumentPreSavingAs; /** * call base implementation for real work */ return KTextEditor::Document::saveAs(normalizeUrl(url)); } QString KTextEditor::DocumentPrivate::defaultDictionary() const { return m_defaultDictionary; } QList > KTextEditor::DocumentPrivate::dictionaryRanges() const { return m_dictionaryRanges; } void KTextEditor::DocumentPrivate::clearDictionaryRanges() { for (QList >::iterator i = m_dictionaryRanges.begin(); i != m_dictionaryRanges.end(); ++i) { delete(*i).first; } m_dictionaryRanges.clear(); if (m_onTheFlyChecker) { m_onTheFlyChecker->refreshSpellCheck(); } emit dictionaryRangesPresent(false); } void KTextEditor::DocumentPrivate::setDictionary(const QString &newDictionary, const KTextEditor::Range &range) { KTextEditor::Range newDictionaryRange = range; if (!newDictionaryRange.isValid() || newDictionaryRange.isEmpty()) { return; } QList > newRanges; // all ranges is 'm_dictionaryRanges' are assumed to be mutually disjoint for (QList >::iterator i = m_dictionaryRanges.begin(); i != m_dictionaryRanges.end();) { qCDebug(LOG_KTE) << "new iteration" << newDictionaryRange; if (newDictionaryRange.isEmpty()) { break; } QPair pair = *i; QString dictionarySet = pair.second; KTextEditor::MovingRange *dictionaryRange = pair.first; qCDebug(LOG_KTE) << *dictionaryRange << dictionarySet; if (dictionaryRange->contains(newDictionaryRange) && newDictionary == dictionarySet) { qCDebug(LOG_KTE) << "dictionaryRange contains newDictionaryRange"; return; } if (newDictionaryRange.contains(*dictionaryRange)) { delete dictionaryRange; i = m_dictionaryRanges.erase(i); qCDebug(LOG_KTE) << "newDictionaryRange contains dictionaryRange"; continue; } KTextEditor::Range intersection = dictionaryRange->toRange().intersect(newDictionaryRange); if (!intersection.isEmpty() && intersection.isValid()) { if (dictionarySet == newDictionary) { // we don't have to do anything for 'intersection' // except cut off the intersection QList remainingRanges = KateSpellCheckManager::rangeDifference(newDictionaryRange, intersection); Q_ASSERT(remainingRanges.size() == 1); newDictionaryRange = remainingRanges.first(); ++i; qCDebug(LOG_KTE) << "dictionarySet == newDictionary"; continue; } QList remainingRanges = KateSpellCheckManager::rangeDifference(*dictionaryRange, intersection); for (QList::iterator j = remainingRanges.begin(); j != remainingRanges.end(); ++j) { KTextEditor::MovingRange *remainingRange = newMovingRange(*j, KTextEditor::MovingRange::ExpandLeft | KTextEditor::MovingRange::ExpandRight); remainingRange->setFeedback(this); newRanges.push_back(QPair(remainingRange, dictionarySet)); } i = m_dictionaryRanges.erase(i); delete dictionaryRange; } else { ++i; } } m_dictionaryRanges += newRanges; if (!newDictionaryRange.isEmpty() && !newDictionary.isEmpty()) { // we don't add anything for the default dictionary KTextEditor::MovingRange *newDictionaryMovingRange = newMovingRange(newDictionaryRange, KTextEditor::MovingRange::ExpandLeft | KTextEditor::MovingRange::ExpandRight); newDictionaryMovingRange->setFeedback(this); m_dictionaryRanges.push_back(QPair(newDictionaryMovingRange, newDictionary)); } if (m_onTheFlyChecker && !newDictionaryRange.isEmpty()) { m_onTheFlyChecker->refreshSpellCheck(newDictionaryRange); } emit dictionaryRangesPresent(!m_dictionaryRanges.isEmpty()); } void KTextEditor::DocumentPrivate::revertToDefaultDictionary(const KTextEditor::Range &range) { setDictionary(QString(), range); } void KTextEditor::DocumentPrivate::setDefaultDictionary(const QString &dict) { if (m_defaultDictionary == dict) { return; } m_defaultDictionary = dict; if (m_onTheFlyChecker) { m_onTheFlyChecker->updateConfig(); refreshOnTheFlyCheck(); } emit defaultDictionaryChanged(this); } void KTextEditor::DocumentPrivate::onTheFlySpellCheckingEnabled(bool enable) { if (isOnTheFlySpellCheckingEnabled() == enable) { return; } if (enable) { Q_ASSERT(m_onTheFlyChecker == nullptr); m_onTheFlyChecker = new KateOnTheFlyChecker(this); } else { delete m_onTheFlyChecker; m_onTheFlyChecker = nullptr; } foreach (KTextEditor::ViewPrivate *view, m_views) { view->reflectOnTheFlySpellCheckStatus(enable); } } bool KTextEditor::DocumentPrivate::isOnTheFlySpellCheckingEnabled() const { return m_onTheFlyChecker != nullptr; } QString KTextEditor::DocumentPrivate::dictionaryForMisspelledRange(const KTextEditor::Range &range) const { if (!m_onTheFlyChecker) { return QString(); } else { return m_onTheFlyChecker->dictionaryForMisspelledRange(range); } } void KTextEditor::DocumentPrivate::clearMisspellingForWord(const QString &word) { if (m_onTheFlyChecker) { m_onTheFlyChecker->clearMisspellingForWord(word); } } void KTextEditor::DocumentPrivate::refreshOnTheFlyCheck(const KTextEditor::Range &range) { if (m_onTheFlyChecker) { m_onTheFlyChecker->refreshSpellCheck(range); } } void KTextEditor::DocumentPrivate::rangeInvalid(KTextEditor::MovingRange *movingRange) { deleteDictionaryRange(movingRange); } void KTextEditor::DocumentPrivate::rangeEmpty(KTextEditor::MovingRange *movingRange) { deleteDictionaryRange(movingRange); } void KTextEditor::DocumentPrivate::deleteDictionaryRange(KTextEditor::MovingRange *movingRange) { qCDebug(LOG_KTE) << "deleting" << movingRange; auto finder = [=] (const QPair& item) -> bool { return item.first == movingRange; }; auto it = std::find_if(m_dictionaryRanges.begin(), m_dictionaryRanges.end(), finder); if (it != m_dictionaryRanges.end()) { m_dictionaryRanges.erase(it); delete movingRange; } Q_ASSERT(std::find_if(m_dictionaryRanges.begin(), m_dictionaryRanges.end(), finder) == m_dictionaryRanges.end()); } bool KTextEditor::DocumentPrivate::containsCharacterEncoding(const KTextEditor::Range &range) { KateHighlighting *highlighting = highlight(); Kate::TextLine textLine; const int rangeStartLine = range.start().line(); const int rangeStartColumn = range.start().column(); const int rangeEndLine = range.end().line(); const int rangeEndColumn = range.end().column(); for (int line = range.start().line(); line <= rangeEndLine; ++line) { textLine = kateTextLine(line); int startColumn = (line == rangeStartLine) ? rangeStartColumn : 0; int endColumn = (line == rangeEndLine) ? rangeEndColumn : textLine->length(); for (int col = startColumn; col < endColumn; ++col) { int attr = textLine->attribute(col); const KatePrefixStore &prefixStore = highlighting->getCharacterEncodingsPrefixStore(attr); if (!prefixStore.findPrefix(textLine, col).isEmpty()) { return true; } } } return false; } int KTextEditor::DocumentPrivate::computePositionWrtOffsets(const OffsetList &offsetList, int pos) { int previousOffset = 0; for (OffsetList::const_iterator i = offsetList.begin(); i != offsetList.end(); ++i) { if ((*i).first > pos) { break; } previousOffset = (*i).second; } return pos + previousOffset; } QString KTextEditor::DocumentPrivate::decodeCharacters(const KTextEditor::Range &range, KTextEditor::DocumentPrivate::OffsetList &decToEncOffsetList, KTextEditor::DocumentPrivate::OffsetList &encToDecOffsetList) { QString toReturn; KTextEditor::Cursor previous = range.start(); int decToEncCurrentOffset = 0, encToDecCurrentOffset = 0; int i = 0; int newI = 0; KateHighlighting *highlighting = highlight(); Kate::TextLine textLine; const int rangeStartLine = range.start().line(); const int rangeStartColumn = range.start().column(); const int rangeEndLine = range.end().line(); const int rangeEndColumn = range.end().column(); for (int line = range.start().line(); line <= rangeEndLine; ++line) { textLine = kateTextLine(line); int startColumn = (line == rangeStartLine) ? rangeStartColumn : 0; int endColumn = (line == rangeEndLine) ? rangeEndColumn : textLine->length(); for (int col = startColumn; col < endColumn;) { int attr = textLine->attribute(col); const KatePrefixStore &prefixStore = highlighting->getCharacterEncodingsPrefixStore(attr); const QHash &characterEncodingsHash = highlighting->getCharacterEncodings(attr); QString matchingPrefix = prefixStore.findPrefix(textLine, col); if (!matchingPrefix.isEmpty()) { toReturn += text(KTextEditor::Range(previous, KTextEditor::Cursor(line, col))); const QChar &c = characterEncodingsHash.value(matchingPrefix); const bool isNullChar = c.isNull(); if (!c.isNull()) { toReturn += c; } i += matchingPrefix.length(); col += matchingPrefix.length(); previous = KTextEditor::Cursor(line, col); decToEncCurrentOffset = decToEncCurrentOffset - (isNullChar ? 0 : 1) + matchingPrefix.length(); encToDecCurrentOffset = encToDecCurrentOffset - matchingPrefix.length() + (isNullChar ? 0 : 1); newI += (isNullChar ? 0 : 1); decToEncOffsetList.push_back(QPair(newI, decToEncCurrentOffset)); encToDecOffsetList.push_back(QPair(i, encToDecCurrentOffset)); continue; } ++col; ++i; ++newI; } ++i; ++newI; } if (previous < range.end()) { toReturn += text(KTextEditor::Range(previous, range.end())); } return toReturn; } void KTextEditor::DocumentPrivate::replaceCharactersByEncoding(const KTextEditor::Range &range) { KateHighlighting *highlighting = highlight(); Kate::TextLine textLine; const int rangeStartLine = range.start().line(); const int rangeStartColumn = range.start().column(); const int rangeEndLine = range.end().line(); const int rangeEndColumn = range.end().column(); for (int line = range.start().line(); line <= rangeEndLine; ++line) { textLine = kateTextLine(line); int startColumn = (line == rangeStartLine) ? rangeStartColumn : 0; int endColumn = (line == rangeEndLine) ? rangeEndColumn : textLine->length(); for (int col = startColumn; col < endColumn;) { int attr = textLine->attribute(col); const QHash &reverseCharacterEncodingsHash = highlighting->getReverseCharacterEncodings(attr); QHash::const_iterator it = reverseCharacterEncodingsHash.find(textLine->at(col)); if (it != reverseCharacterEncodingsHash.end()) { replaceText(KTextEditor::Range(line, col, line, col + 1), *it); col += (*it).length(); continue; } ++col; } } } // // Highlighting information // KTextEditor::Attribute::Ptr KTextEditor::DocumentPrivate::attributeAt(const KTextEditor::Cursor &position) { KTextEditor::Attribute::Ptr attrib(new KTextEditor::Attribute()); KTextEditor::ViewPrivate *view = m_views.empty() ? nullptr : m_views.begin().value(); if (!view) { qCWarning(LOG_KTE) << "ATTENTION: cannot access lineAttributes() without any View (will be fixed eventually)"; return attrib; } Kate::TextLine kateLine = kateTextLine(position.line()); if (!kateLine) { return attrib; } *attrib = *view->renderer()->attribute(kateLine->attribute(position.column())); return attrib; } QStringList KTextEditor::DocumentPrivate::embeddedHighlightingModes() const { return highlight()->getEmbeddedHighlightingModes(); } QString KTextEditor::DocumentPrivate::highlightingModeAt(const KTextEditor::Cursor &position) { Kate::TextLine kateLine = kateTextLine(position.line()); // const QVector< short >& attrs = kateLine->ctxArray(); // qCDebug(LOG_KTE) << "----------------------------------------------------------------------"; // foreach( short a, attrs ) { // qCDebug(LOG_KTE) << a; // } // qCDebug(LOG_KTE) << "----------------------------------------------------------------------"; // qCDebug(LOG_KTE) << "col: " << position.column() << " lastchar:" << kateLine->lastChar() << " length:" << kateLine->length() << "global mode:" << highlightingMode(); int len = kateLine->length(); int pos = position.column(); if (pos >= len) { const Kate::TextLineData::ContextStack &ctxs = kateLine->contextStack(); int ctxcnt = ctxs.count(); if (ctxcnt == 0) { return highlightingMode(); } int ctx = ctxs.at(ctxcnt - 1); if (ctx == 0) { return highlightingMode(); } return KateHlManager::self()->nameForIdentifier(highlight()->hlKeyForContext(ctx)); } int attr = kateLine->attribute(pos); if (attr == 0) { return mode(); } return KateHlManager::self()->nameForIdentifier(highlight()->hlKeyForAttrib(attr)); } Kate::SwapFile *KTextEditor::DocumentPrivate::swapFile() { return m_swapfile; } /** * \return \c -1 if \c line or \c column invalid, otherwise one of * standard style attribute number */ int KTextEditor::DocumentPrivate::defStyleNum(int line, int column) { // Validate parameters to prevent out of range access if (line < 0 || line >= lines() || column < 0) { return -1; } // get highlighted line Kate::TextLine tl = kateTextLine(line); // make sure the textline is a valid pointer if (!tl) { return -1; } /** * either get char attribute or attribute of context still active at end of line */ int attribute = 0; if (column < tl->length()) { attribute = tl->attribute(column); } else if (column == tl->length()) { KateHlContext *context = tl->contextStack().isEmpty() ? highlight()->contextNum(0) : highlight()->contextNum(tl->contextStack().back()); attribute = context->attr; } else { return -1; } return highlight()->defaultStyleForAttribute(attribute); } bool KTextEditor::DocumentPrivate::isComment(int line, int column) { const int defaultStyle = defStyleNum(line, column); return defaultStyle == KTextEditor::dsComment; } int KTextEditor::DocumentPrivate::findTouchedLine(int startLine, bool down) { const int offset = down ? 1 : -1; const int lineCount = lines(); while (startLine >= 0 && startLine < lineCount) { Kate::TextLine tl = m_buffer->plainLine(startLine); if (tl && (tl->markedAsModified() || tl->markedAsSavedOnDisk())) { return startLine; } startLine += offset; } return -1; } void KTextEditor::DocumentPrivate::setActiveTemplateHandler(KateTemplateHandler* handler) { // delete any active template handler delete m_activeTemplateHandler.data(); m_activeTemplateHandler = handler; } //BEGIN KTextEditor::MessageInterface bool KTextEditor::DocumentPrivate::postMessage(KTextEditor::Message *message) { // no message -> cancel if (!message) { return false; } // make sure the desired view belongs to this document if (message->view() && message->view()->document() != this) { qCWarning(LOG_KTE) << "trying to post a message to a view of another document:" << message->text(); return false; } message->setParent(this); message->setDocument(this); // if there are no actions, add a close action by default if widget does not auto-hide if (message->actions().count() == 0 && message->autoHide() < 0) { QAction *closeAction = new QAction(QIcon::fromTheme(QStringLiteral("window-close")), i18n("&Close"), nullptr); closeAction->setToolTip(i18n("Close message")); message->addAction(closeAction); } // make sure the message is registered even if no actions and no views exist m_messageHash[message] = QList >(); // reparent actions, as we want full control over when they are deleted foreach (QAction *action, message->actions()) { action->setParent(nullptr); m_messageHash[message].append(QSharedPointer(action)); } // post message to requested view, or to all views if (KTextEditor::ViewPrivate *view = qobject_cast(message->view())) { view->postMessage(message, m_messageHash[message]); } else { foreach (KTextEditor::ViewPrivate *view, m_views) { view->postMessage(message, m_messageHash[message]); } } // also catch if the user manually calls delete message connect(message, SIGNAL(closed(KTextEditor::Message*)), SLOT(messageDestroyed(KTextEditor::Message*))); return true; } void KTextEditor::DocumentPrivate::messageDestroyed(KTextEditor::Message *message) { // KTE:Message is already in destructor Q_ASSERT(m_messageHash.contains(message)); m_messageHash.remove(message); } //END KTextEditor::MessageInterface void KTextEditor::DocumentPrivate::closeDocumentInApplication() { KTextEditor::EditorPrivate::self()->application()->closeDocument(this); } diff --git a/src/document/katedocument.h b/src/document/katedocument.h index 73f2e829..225c0f90 100644 --- a/src/document/katedocument.h +++ b/src/document/katedocument.h @@ -1,1410 +1,1410 @@ /* This file is part of the KDE libraries Copyright (C) 2001-2004 Christoph Cullmann Copyright (C) 2001 Joseph Wenninger Copyright (C) 1999 Jochen Wilhelmy Copyright (C) 2006 Hamish Rodda 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_DOCUMENT_H_ #define _KATE_DOCUMENT_H_ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "katetextline.h" class KateTemplateHandler; namespace KTextEditor { class Plugin; class Attribute; class TemplateScript; } namespace KIO { class TransferJob; } namespace Kate { class SwapFile; } class KateBuffer; namespace KTextEditor { class ViewPrivate; } class KateDocumentConfig; class KateHighlighting; class KateUndoManager; class KateOnTheFlyChecker; class KateDocumentTest; class KateAutoIndent; class KateModOnHdPrompt; /** * @brief Backend of KTextEditor::Document related public KTextEditor interfaces. * * @warning This file is @e private API and not part of the public * KTextEditor interfaces. */ class KTEXTEDITOR_EXPORT KTextEditor::DocumentPrivate : public KTextEditor::Document, public KTextEditor::MarkInterface, public KTextEditor::ModificationInterface, public KTextEditor::ConfigInterface, public KTextEditor::AnnotationInterface, public KTextEditor::MovingInterface, private KTextEditor::MovingRangeFeedback { Q_OBJECT Q_INTERFACES(KTextEditor::MarkInterface) Q_INTERFACES(KTextEditor::ModificationInterface) Q_INTERFACES(KTextEditor::AnnotationInterface) Q_INTERFACES(KTextEditor::ConfigInterface) Q_INTERFACES(KTextEditor::MovingInterface) friend class KTextEditor::Document; friend class ::KateDocumentTest; friend class ::KateBuffer; public: explicit DocumentPrivate(bool bSingleViewMode = false, bool bReadOnly = false, QWidget *parentWidget = nullptr, QObject * = nullptr); - ~DocumentPrivate(); + ~DocumentPrivate() Q_DECL_OVERRIDE; using ReadWritePart::closeUrl; bool closeUrl() Q_DECL_OVERRIDE; bool openUrl(const QUrl &url) Q_DECL_OVERRIDE; KTextEditor::Range rangeOnLine(KTextEditor::Range range, int line) const; private: void showAndSetOpeningErrorAccess(); /* * Overload this to have on-demand view creation */ public: /** * @return The widget defined by this part, set by setWidget(). */ QWidget *widget() Q_DECL_OVERRIDE; public: bool readOnly() const { return m_bReadOnly; } bool singleViewMode() const { return m_bSingleViewMode; } private: // only to make part work, don't change it ! const bool m_bSingleViewMode; const bool m_bReadOnly; // // KTextEditor::Document stuff // public: KTextEditor::View *createView(QWidget *parent, KTextEditor::MainWindow *mainWindow = nullptr) Q_DECL_OVERRIDE; QList views() const Q_DECL_OVERRIDE { return m_views.keys(); } virtual KTextEditor::View *activeView() const { return m_activeView; } private: QHash m_views; KTextEditor::View *m_activeView = nullptr; // // KTextEditor::EditInterface stuff // public Q_SLOTS: bool setText(const QString &) Q_DECL_OVERRIDE; bool setText(const QStringList &text) Q_DECL_OVERRIDE; bool clear() Q_DECL_OVERRIDE; bool insertText(const KTextEditor::Cursor &position, const QString &s, bool block = false) Q_DECL_OVERRIDE; bool insertText(const KTextEditor::Cursor &position, const QStringList &text, bool block = false) Q_DECL_OVERRIDE; bool insertLine(int line, const QString &s) Q_DECL_OVERRIDE; bool insertLines(int line, const QStringList &s) Q_DECL_OVERRIDE; bool removeText(const KTextEditor::Range &range, bool block = false) Q_DECL_OVERRIDE; bool removeLine(int line) Q_DECL_OVERRIDE; bool replaceText(const KTextEditor::Range &range, const QString &s, bool block = false) Q_DECL_OVERRIDE; // unhide method... bool replaceText(const KTextEditor::Range &r, const QStringList &l, bool b) Q_DECL_OVERRIDE { return KTextEditor::Document::replaceText(r, l, b); } public: bool isEditingTransactionRunning() const Q_DECL_OVERRIDE; QString text(const KTextEditor::Range &range, bool blockwise = false) const Q_DECL_OVERRIDE; QStringList textLines(const KTextEditor::Range &range, bool block = false) const Q_DECL_OVERRIDE; QString text() const Q_DECL_OVERRIDE; QString line(int line) const Q_DECL_OVERRIDE; QChar characterAt(const KTextEditor::Cursor &position) const Q_DECL_OVERRIDE; QString wordAt(const KTextEditor::Cursor &cursor) const Q_DECL_OVERRIDE; KTextEditor::Range wordRangeAt(const KTextEditor::Cursor &cursor) const Q_DECL_OVERRIDE; bool isValidTextPosition(const KTextEditor::Cursor& cursor) const Q_DECL_OVERRIDE; int lines() const Q_DECL_OVERRIDE; bool isLineModified(int line) const Q_DECL_OVERRIDE; bool isLineSaved(int line) const Q_DECL_OVERRIDE; bool isLineTouched(int line) const Q_DECL_OVERRIDE; KTextEditor::Cursor documentEnd() const Q_DECL_OVERRIDE; int totalCharacters() const Q_DECL_OVERRIDE; int lineLength(int line) const Q_DECL_OVERRIDE; Q_SIGNALS: void charactersSemiInteractivelyInserted(const KTextEditor::Cursor &position, const QString &text); /** * The \p document emits this signal whenever text was inserted. The * insertion occurred at range.start(), and new text now occupies up to * range.end(). * \param document document which emitted this signal * \param range range that the newly inserted text occupies * \see insertText(), insertLine() */ void textInserted(KTextEditor::Document *document, const KTextEditor::Range &range); /** * The \p document emits this signal whenever \p range was removed, i.e. * text was removed. * \param document document which emitted this signal * \param range range that the removed text previously occupied * \param oldText the text that has been removed * \see removeText(), removeLine(), clear() */ void textRemoved(KTextEditor::Document *document, const KTextEditor::Range &range, const QString &oldText); public: //BEGIN editStart/editEnd (start, end, undo, cursor update, view update) /** * Enclose editor actions with @p editStart() and @p editEnd() to group * them. */ bool editStart(); /** * Alias for @p editStart() */ void editBegin() { editStart(); } /** * End a editor operation. * @see editStart() */ bool editEnd(); void pushEditState(); void popEditState(); virtual bool startEditing() { return editStart(); } virtual bool finishEditing() { return editEnd(); } //END editStart/editEnd void inputMethodStart(); void inputMethodEnd(); //BEGIN LINE BASED INSERT/REMOVE STUFF (editStart() and editEnd() included) /** * Add a string in the given line/column * @param line line number * @param col column * @param s string to be inserted * @return true on success */ bool editInsertText(int line, int col, const QString &s); /** * Remove a string in the given line/column * @param line line number * @param col column * @param len length of text to be removed * @return true on success */ bool editRemoveText(int line, int col, int len); /** * Mark @p line as @p autowrapped. This is necessary if static word warp is * enabled, because we have to know whether to insert a new line or add the * wrapped words to the followin line. * @param line line number * @param autowrapped autowrapped? * @return true on success */ bool editMarkLineAutoWrapped(int line, bool autowrapped); /** * Wrap @p line. If @p newLine is true, ignore the textline's flag * KateTextLine::flagAutoWrapped and force a new line. Whether a new line * was needed/added you can grab with @p newLineAdded. * @param line line number * @param col column * @param newLine if true, force a new line * @param newLineAdded return value is true, if new line was added (may be 0) * @return true on success */ bool editWrapLine(int line, int col, bool newLine = true, bool *newLineAdded = nullptr); /** * Unwrap @p line. If @p removeLine is true, we force to join the lines. If * @p removeLine is true, @p length is ignored (eg not needed). * @param line line number * @param removeLine if true, force to remove the next line * @return true on success */ bool editUnWrapLine(int line, bool removeLine = true, int length = 0); /** * Insert a string at the given line. * @param line line number * @param s string to insert * @return true on success */ bool editInsertLine(int line, const QString &s); /** * Remove a line * @param line line number * @return true on success */ bool editRemoveLine(int line); bool editRemoveLines(int from, int to); /** * Remove a line * @param startLine line to begin wrapping * @param endLine line to stop wrapping * @return true on success */ bool wrapText(int startLine, int endLine); //END LINE BASED INSERT/REMOVE STUFF Q_SIGNALS: /** * Emmitted when text from @p line was wrapped at position pos onto line @p nextLine. */ void editLineWrapped(int line, int col, int len); /** * Emitted each time text from @p nextLine was upwrapped onto @p line. */ void editLineUnWrapped(int line, int col); public: bool isEditRunning() const; void setUndoMergeAllEdits(bool merge); enum EditingPositionKind { Previous, Next }; /** *Returns the next or previous position cursor in this document from the stack depending on the argument passed. *@return cursor invalid if m_editingStack empty */ KTextEditor::Cursor lastEditingPosition(EditingPositionKind nextOrPrevious, KTextEditor::Cursor); private: int editSessionNumber = 0; QStack editStateStack; bool editIsRunning = false; bool m_undoMergeAllEdits = false; QStack> m_editingStack; int m_editingStackPosition = -1; static const int s_editingStackSizeLimit = 32; // // KTextEditor::UndoInterface stuff // public Q_SLOTS: void undo(); void redo(); /** * Removes all the elements in m_editingStack of the respective document. */ void clearEditingPosStack(); /** * Saves the editing positions into the stack. * If the consecutive editings happens in the same line, then remove * the previous and add the new one with updated column no. */ void saveEditingPositions(KTextEditor::Document *, const KTextEditor::Range &range); public: uint undoCount() const; uint redoCount() const; KateUndoManager *undoManager() { return m_undoManager; } protected: KateUndoManager *const m_undoManager; Q_SIGNALS: void undoChanged(); public: QVector searchText( const KTextEditor::Range &range, const QString &pattern, const KTextEditor::SearchOptions options) const; private: /** * Return a widget suitable to be used as a dialog parent. */ QWidget *dialogParent(); /* * Access to the mode/highlighting subsystem */ public: /** * @copydoc KTextEditor::Document::defaultStyleAt() */ KTextEditor::DefaultStyle defaultStyleAt(const KTextEditor::Cursor &position) const Q_DECL_OVERRIDE; /** * Return the name of the currently used mode * \return name of the used mode */ QString mode() const Q_DECL_OVERRIDE; /** * Return the name of the currently used mode * \return name of the used mode */ QString highlightingMode() const Q_DECL_OVERRIDE; /** * Return a list of the names of all possible modes * \return list of mode names */ QStringList modes() const Q_DECL_OVERRIDE; /** * Return a list of the names of all possible modes * \return list of mode names */ QStringList highlightingModes() const Q_DECL_OVERRIDE; /** * Set the current mode of the document by giving its name * \param name name of the mode to use for this document * \return \e true on success, otherwise \e false */ bool setMode(const QString &name) Q_DECL_OVERRIDE; /** * Set the current mode of the document by giving its name * \param name name of the mode to use for this document * \return \e true on success, otherwise \e false */ bool setHighlightingMode(const QString &name) Q_DECL_OVERRIDE; /** * Returns the name of the section for a highlight given its @p index in the highlight * list (as returned by highlightModes()). * You can use this function to build a tree of the highlight names, organized in sections. * \param index in the highlight list for which to find the section name. */ QString highlightingModeSection(int index) const Q_DECL_OVERRIDE; /** * Returns the name of the section for a mode given its @p index in the highlight * list (as returned by modes()). * You can use this function to build a tree of the mode names, organized in sections. * \param index index in the highlight list for which to find the section name. */ QString modeSection(int index) const Q_DECL_OVERRIDE; /* * Helpers.... */ public: void bufferHlChanged(); /** * allow to mark, that we changed hl on user wish and should not reset it * atm used for the user visible menu to select highlightings */ void setDontChangeHlOnSave(); /** * Set that the BOM marker is forced via the tool menu */ void bomSetByUser(); public: /** * Read session settings from the given \p config. * * Known flags: * "SkipUrl" => don't save/restore the file * "SkipMode" => don't save/restore the mode * "SkipHighlighting" => don't save/restore the highlighting * "SkipEncoding" => don't save/restore the encoding * * \param config read the session settings from this KConfigGroup * \param flags additional flags * \see writeSessionConfig() */ void readSessionConfig(const KConfigGroup &config, const QSet &flags = QSet()) Q_DECL_OVERRIDE; /** * Write session settings to the \p config. * See readSessionConfig() for more details. * * \param config write the session settings to this KConfigGroup * \param flags additional flags * \see readSessionConfig() */ void writeSessionConfig(KConfigGroup &config, const QSet &flags = QSet()) Q_DECL_OVERRIDE; Q_SIGNALS: void configChanged(); // // KTextEditor::MarkInterface // public Q_SLOTS: void setMark(int line, uint markType) Q_DECL_OVERRIDE; void clearMark(int line) Q_DECL_OVERRIDE; void addMark(int line, uint markType) Q_DECL_OVERRIDE; void removeMark(int line, uint markType) Q_DECL_OVERRIDE; void clearMarks() Q_DECL_OVERRIDE; void requestMarkTooltip(int line, QPoint position); ///Returns true if the click on the mark should not be further processed bool handleMarkClick(int line); ///Returns true if the context-menu event should not further be processed bool handleMarkContextMenu(int line, QPoint position); void setMarkPixmap(MarkInterface::MarkTypes, const QPixmap &) Q_DECL_OVERRIDE; void setMarkDescription(MarkInterface::MarkTypes, const QString &) Q_DECL_OVERRIDE; void setEditableMarks(uint markMask) Q_DECL_OVERRIDE; public: uint mark(int line) Q_DECL_OVERRIDE; const QHash &marks() Q_DECL_OVERRIDE; QPixmap markPixmap(MarkInterface::MarkTypes) const Q_DECL_OVERRIDE; QString markDescription(MarkInterface::MarkTypes) const Q_DECL_OVERRIDE; virtual QColor markColor(MarkInterface::MarkTypes) const; uint editableMarks() const Q_DECL_OVERRIDE; Q_SIGNALS: void markToolTipRequested(KTextEditor::Document *document, KTextEditor::Mark mark, QPoint position, bool &handled); void markContextMenuRequested(KTextEditor::Document *document, KTextEditor::Mark mark, QPoint pos, bool &handled); void markClicked(KTextEditor::Document *document, KTextEditor::Mark mark, bool &handled); void marksChanged(KTextEditor::Document *) Q_DECL_OVERRIDE; void markChanged(KTextEditor::Document *, KTextEditor::Mark, KTextEditor::MarkInterface::MarkChangeAction) Q_DECL_OVERRIDE; private: QHash m_marks; QHash m_markPixmaps; QHash m_markDescriptions; uint m_editableMarks = markType01; // KTextEditor::PrintInterface // public Q_SLOTS: bool print() Q_DECL_OVERRIDE; void printPreview() Q_DECL_OVERRIDE; // // KTextEditor::DocumentInfoInterface ( ### unfinished ) // public: /** * Tries to detect mime-type based on file name and content of buffer. * * @return the name of the mimetype for the document. */ QString mimeType() Q_DECL_OVERRIDE; // // once was KTextEditor::VariableInterface // public: /** * Returns the value for the variable @p name. * If the Document does not have a variable called @p name, * an empty QString() is returned. * * @param name variable to query * @return value of the variable @p name * @see setVariable() */ virtual QString variable(const QString &name) const; /** * Set the variable @p name to @p value. Setting and changing a variable * has immediate effect on the Document. For instance, setting the variable * @e indent-mode to @e cstyle will immediately cause the Document to load * the C Style indenter. * * @param name the variable name * @param value the value to be set * @see variable() */ virtual void setVariable(const QString &name, const QString &value); private: QMap m_storedVariables; // // MovingInterface API // public: /** * Create a new moving cursor for this document. * @param position position of the moving cursor to create * @param insertBehavior insertion behavior * @return new moving cursor for the document */ KTextEditor::MovingCursor *newMovingCursor(const KTextEditor::Cursor &position, KTextEditor::MovingCursor::InsertBehavior insertBehavior = KTextEditor::MovingCursor::MoveOnInsert) Q_DECL_OVERRIDE; /** * Create a new moving range for this document. * @param range range of the moving range to create * @param insertBehaviors insertion behaviors * @param emptyBehavior behavior on becoming empty * @return new moving range for the document */ KTextEditor::MovingRange *newMovingRange(const KTextEditor::Range &range, KTextEditor::MovingRange::InsertBehaviors insertBehaviors = KTextEditor::MovingRange::DoNotExpand , KTextEditor::MovingRange::EmptyBehavior emptyBehavior = KTextEditor::MovingRange::AllowEmpty) Q_DECL_OVERRIDE; /** * Current revision * @return current revision */ qint64 revision() const Q_DECL_OVERRIDE; /** * Last revision the buffer got successful saved * @return last revision buffer got saved, -1 if none */ qint64 lastSavedRevision() const Q_DECL_OVERRIDE; /** * Lock a revision, this will keep it around until released again. * But all revisions will always be cleared on buffer clear() (and therefor load()) * @param revision revision to lock */ void lockRevision(qint64 revision) Q_DECL_OVERRIDE; /** * Release a revision. * @param revision revision to release */ void unlockRevision(qint64 revision) Q_DECL_OVERRIDE; /** * Transform a cursor from one revision to an other. * @param cursor cursor to transform * @param insertBehavior behavior of this cursor on insert of text at its position * @param fromRevision from this revision we want to transform * @param toRevision to this revision we want to transform, default of -1 is current revision */ void transformCursor(KTextEditor::Cursor &cursor, KTextEditor::MovingCursor::InsertBehavior insertBehavior, qint64 fromRevision, qint64 toRevision = -1) Q_DECL_OVERRIDE; /** * Transform a cursor from one revision to an other. * @param line line number of the cursor to transform * @param column column number of the cursor to transform * @param insertBehavior behavior of this cursor on insert of text at its position * @param fromRevision from this revision we want to transform * @param toRevision to this revision we want to transform, default of -1 is current revision */ void transformCursor(int &line, int &column, KTextEditor::MovingCursor::InsertBehavior insertBehavior, qint64 fromRevision, qint64 toRevision = -1) Q_DECL_OVERRIDE; /** * Transform a range from one revision to an other. * @param range range to transform * @param insertBehaviors behavior of this range on insert of text at its position * @param emptyBehavior behavior on becoming empty * @param fromRevision from this revision we want to transform * @param toRevision to this revision we want to transform, default of -1 is current revision */ void transformRange(KTextEditor::Range &range, KTextEditor::MovingRange::InsertBehaviors insertBehaviors, KTextEditor::MovingRange::EmptyBehavior emptyBehavior, qint64 fromRevision, qint64 toRevision = -1) Q_DECL_OVERRIDE; // // MovingInterface Signals // Q_SIGNALS: /** * This signal is emitted before the cursors/ranges/revisions of a document are destroyed as the document is deleted. * @param document the document which the interface belongs too which is in the process of being deleted */ void aboutToDeleteMovingInterfaceContent(KTextEditor::Document *document); /** * This signal is emitted before the ranges of a document are invalidated and the revisions are deleted as the document is cleared (for example on load/reload). * While this signal is emitted, still the old document content is around before the clear. * @param document the document which the interface belongs too which will invalidate its data */ void aboutToInvalidateMovingInterfaceContent(KTextEditor::Document *document); // // Annotation Interface // public: void setAnnotationModel(KTextEditor::AnnotationModel *model) Q_DECL_OVERRIDE; KTextEditor::AnnotationModel *annotationModel() const Q_DECL_OVERRIDE; Q_SIGNALS: void annotationModelChanged(KTextEditor::AnnotationModel *, KTextEditor::AnnotationModel *); private: KTextEditor::AnnotationModel *m_annotationModel = nullptr; // // KParts::ReadWrite stuff // public: /** * open the file obtained by the kparts framework * the framework abstracts the loading of remote files * @return success */ bool openFile() Q_DECL_OVERRIDE; /** * save the file obtained by the kparts framework * the framework abstracts the uploading of remote files * @return success */ bool saveFile() Q_DECL_OVERRIDE; void setReadWrite(bool rw = true) Q_DECL_OVERRIDE; void setModified(bool m) Q_DECL_OVERRIDE; private: void activateDirWatch(const QString &useFileName = QString()); void deactivateDirWatch(); QString m_dirWatchFile; /** * Make backup copy during saveFile, if configured that way. * @return success? else saveFile should return false and not write the file */ bool createBackupFile(); public: /** * Type chars in a view. * Will filter out non-printable chars from the realChars array before inserting. */ bool typeChars(KTextEditor::ViewPrivate *type, const QString &realChars); /** * gets the last line number (lines() - 1) */ inline int lastLine() const { return lines() - 1; } // Repaint all of all of the views void repaintViews(bool paintOnlyDirty = true); KateHighlighting *highlight() const; public Q_SLOTS: void tagLines(int start, int end); private Q_SLOTS: void internalHlChanged(); public: void addView(KTextEditor::View *); /** removes the view from the list of views. The view is *not* deleted. * That's your job. Or, easier, just delete the view in the first place. * It will remove itself. TODO: this could be converted to a private slot * connected to the view's destroyed() signal. It is not currently called * anywhere except from the KTextEditor::ViewPrivate destructor. */ void removeView(KTextEditor::View *); void setActiveView(KTextEditor::View *); bool ownedView(KTextEditor::ViewPrivate *); int toVirtualColumn(int line, int column) const; int toVirtualColumn(const KTextEditor::Cursor &) const; int fromVirtualColumn(int line, int column) const; int fromVirtualColumn(const KTextEditor::Cursor &) const; void newLine(KTextEditor::ViewPrivate *view); // Changes input void backspace(KTextEditor::ViewPrivate *view, const KTextEditor::Cursor &); void del(KTextEditor::ViewPrivate *view, const KTextEditor::Cursor &); void transpose(const KTextEditor::Cursor &); void paste(KTextEditor::ViewPrivate *view, const QString &text); public: void indent(KTextEditor::Range range, int change); void comment(KTextEditor::ViewPrivate *view, uint line, uint column, int change); void align(KTextEditor::ViewPrivate *view, const KTextEditor::Range &range); void insertTab(KTextEditor::ViewPrivate *view, const KTextEditor::Cursor &); enum TextTransform { Uppercase, Lowercase, Capitalize }; /** Handling uppercase, lowercase and capitalize for the view. If there is a selection, that is transformed, otherwise for uppercase or lowercase the character right of the cursor is transformed, for capitalize the word under the cursor is transformed. */ void transform(KTextEditor::ViewPrivate *view, const KTextEditor::Cursor &, TextTransform); /** Unwrap a range of lines. */ void joinLines(uint first, uint last); private: bool removeStringFromBeginning(int line, const QString &str); bool removeStringFromEnd(int line, const QString &str); /** Expand tabs to spaces in typed text, if enabled. @param cursorPos The current cursor position for the inserted characters. @param str The typed characters to expand. */ QString eventuallyReplaceTabs(const KTextEditor::Cursor &cursorPos, const QString &str) const; /** Find the position (line and col) of the next char that is not a space. If found line and col point to the found character. Otherwise they have both the value -1. @param line Line of the character which is examined first. @param col Column of the character which is examined first. @return True if the specified or a following character is not a space Otherwise false. */ bool nextNonSpaceCharPos(int &line, int &col); /** Find the position (line and col) of the previous char that is not a space. If found line and col point to the found character. Otherwise they have both the value -1. @return True if the specified or a preceding character is not a space. Otherwise false. */ bool previousNonSpaceCharPos(int &line, int &col); /** * Sets a comment marker as defined by the language providing the attribute * @p attrib on the line @p line */ void addStartLineCommentToSingleLine(int line, int attrib = 0); /** * Removes a comment marker as defined by the language providing the attribute * @p attrib on the line @p line */ bool removeStartLineCommentFromSingleLine(int line, int attrib = 0); /** * @see addStartLineCommentToSingleLine. */ void addStartStopCommentToSingleLine(int line, int attrib = 0); /** *@see removeStartLineCommentFromSingleLine. */ bool removeStartStopCommentFromSingleLine(int line, int attrib = 0); /** *@see removeStartLineCommentFromSingleLine. */ bool removeStartStopCommentFromRegion(const KTextEditor::Cursor &start, const KTextEditor::Cursor &end, int attrib = 0); /** * Add a comment marker as defined by the language providing the attribute * @p attrib to each line in the selection. */ void addStartStopCommentToSelection(KTextEditor::ViewPrivate *view, int attrib = 0); /** * @see addStartStopCommentToSelection. */ void addStartLineCommentToSelection(KTextEditor::ViewPrivate *view, int attrib = 0); /** * Removes comment markers relevant to the language providing * the attribuge @p attrib from each line in the selection. * * @return whether the operation succeeded. */ bool removeStartStopCommentFromSelection(KTextEditor::ViewPrivate *view, int attrib = 0); /** * @see removeStartStopCommentFromSelection. */ bool removeStartLineCommentFromSelection(KTextEditor::ViewPrivate *view, int attrib = 0); public: KTextEditor::Range findMatchingBracket(const KTextEditor::Cursor & start, int maxLines); public: QString documentName() const Q_DECL_OVERRIDE { return m_docName; } private: void updateDocName(); public: /** * @return whether the document is modified on disk since last saved */ bool isModifiedOnDisc() { return m_modOnHd; } void setModifiedOnDisk(ModifiedOnDiskReason reason) Q_DECL_OVERRIDE; void setModifiedOnDiskWarning(bool on) Q_DECL_OVERRIDE; public Q_SLOTS: /** * Ask the user what to do, if the file has been modified on disk. * Reimplemented from KTextEditor::Document. */ virtual void slotModifiedOnDisk(KTextEditor::View *v = nullptr); /** * Reloads the current document from disk if possible */ bool documentReload() Q_DECL_OVERRIDE; bool documentSave() Q_DECL_OVERRIDE; bool documentSaveAs() Q_DECL_OVERRIDE; bool documentSaveAsWithEncoding(const QString &encoding); bool documentSaveCopyAs(); bool save() Q_DECL_OVERRIDE; public: bool saveAs(const QUrl &url) Q_DECL_OVERRIDE; Q_SIGNALS: /** * Indicate this file is modified on disk * @param doc the KTextEditor::Document object that represents the file on disk * @param isModified indicates the file was modified rather than created or deleted * @param reason the reason we are emitting the signal. */ void modifiedOnDisk(KTextEditor::Document *doc, bool isModified, KTextEditor::ModificationInterface::ModifiedOnDiskReason reason) Q_DECL_OVERRIDE; private: // helper to handle the embedded notification for externally modified files QPointer m_modOnHdHandler; private Q_SLOTS: void onModOnHdSaveAs(); void onModOnHdReload(); void onModOnHdIgnore(); public: bool setEncoding(const QString &e) Q_DECL_OVERRIDE; QString encoding() const Q_DECL_OVERRIDE; public Q_SLOTS: void setWordWrap(bool on); void setWordWrapAt(uint col); public: bool wordWrap() const; uint wordWrapAt() const; public Q_SLOTS: void setPageUpDownMovesCursor(bool on); public: bool pageUpDownMovesCursor() const; // code folding public: /** * Same as plainKateTextLine(), except that it is made sure * the line is highlighted. */ Kate::TextLine kateTextLine(int i); //! @copydoc KateBuffer::plainLine() Kate::TextLine plainKateTextLine(int i); Q_SIGNALS: void aboutToRemoveText(const KTextEditor::Range &); private Q_SLOTS: void slotModOnHdDirty(const QString &path); void slotModOnHdCreated(const QString &path); void slotModOnHdDeleted(const QString &path); void slotDelayedHandleModOnHd(); private: /** * Create a git compatible sha1 checksum of the file, if it is a local file. * The result can be accessed through KateBuffer::digest(). * * @return wheather the operation was attempted and succeeded. */ bool createDigest(); /** * create a string for the modonhd warnings, giving the reason. */ QString reasonedMOHString() const; /** * Removes all trailing whitespace in the document. */ void removeTrailingSpaces(); public: /** * Returns a git compatible sha1 checksum of this document on disk. * @return checksum for this document on disk */ QByteArray checksum() const Q_DECL_OVERRIDE; void updateFileType(const QString &newType, bool user = false); QString fileType() const { return m_fileType; } /** * Get access to buffer of this document. * Is needed to create cursors and ranges for example. * @return document buffer */ KateBuffer &buffer() { return *m_buffer; } /** * set indentation mode by user * this will remember that a user did set it and will avoid reset on save */ void rememberUserDidSetIndentationMode() { m_indenterSetByUser = true; } /** * User did set encoding for next reload => enforce it! */ void userSetEncodingForNextReload() { m_userSetEncodingForNextReload = true; } // // REALLY internal data ;) // private: // text buffer KateBuffer *const m_buffer; // indenter KateAutoIndent *const m_indenter; bool m_hlSetByUser = false; bool m_bomSetByUser = false; bool m_indenterSetByUser = false; bool m_userSetEncodingForNextReload = false; bool m_modOnHd = false; ModifiedOnDiskReason m_modOnHdReason = OnDiskUnmodified; ModifiedOnDiskReason m_prevModOnHdReason = OnDiskUnmodified; QString m_docName; int m_docNameNumber = 0; // file type !!! QString m_fileType; bool m_fileTypeSetByUser = false; /** * document is still reloading a file */ bool m_reloading = false; public Q_SLOTS: void slotQueryClose_save(bool *handled, bool *abortClosing); public: bool queryClose() Q_DECL_OVERRIDE; void makeAttribs(bool needInvalidate = true); static bool checkOverwrite(QUrl u, QWidget *parent); /** * Configuration */ public: KateDocumentConfig *config() { return m_config; } KateDocumentConfig *config() const { return m_config; } void updateConfig(); private: KateDocumentConfig *const m_config; /** * Variable Reader * TODO add register functionality/ktexteditor interface */ private: /** * read dir config file */ void readDirConfig(); /** Reads all the variables in the document. Called when opening/saving a document */ void readVariables(bool onlyViewAndRenderer = false); /** Reads and applies the variables in a single line TODO registered variables gets saved in a [map] */ void readVariableLine(QString t, bool onlyViewAndRenderer = false); /** Sets a view variable in all the views. */ void setViewVariable(QString var, QString val); /** @return weather a string value could be converted to a bool value as supported. The value is put in *result. */ static bool checkBoolValue(QString value, bool *result); /** @return weather a string value could be converted to a integer value. The value is put in *result. */ static bool checkIntValue(QString value, int *result); /** Feeds value into @p col using QColor::setNamedColor() and returns wheather the color is valid */ static bool checkColorValue(QString value, QColor &col); bool m_fileChangedDialogsActivated = false; // // KTextEditor::ConfigInterface // public: QStringList configKeys() const Q_DECL_OVERRIDE; QVariant configValue(const QString &key) Q_DECL_OVERRIDE; void setConfigValue(const QString &key, const QVariant &value) Q_DECL_OVERRIDE; // // KTextEditor::RecoveryInterface // public: bool isDataRecoveryAvailable() const Q_DECL_OVERRIDE; void recoverData() Q_DECL_OVERRIDE; void discardDataRecovery() Q_DECL_OVERRIDE; // // Highlighting information // public: QStringList embeddedHighlightingModes() const Q_DECL_OVERRIDE; QString highlightingModeAt(const KTextEditor::Cursor &position) Q_DECL_OVERRIDE; // TODO KDE5: move to View virtual KTextEditor::Attribute::Ptr attributeAt(const KTextEditor::Cursor &position); // //BEGIN: KTextEditor::MessageInterface // public: bool postMessage(KTextEditor::Message *message) Q_DECL_OVERRIDE; public Q_SLOTS: void messageDestroyed(KTextEditor::Message *message); private: QHash > > m_messageHash; //END KTextEditor::MessageInterface public: QString defaultDictionary() const; QList > dictionaryRanges() const; bool isOnTheFlySpellCheckingEnabled() const; QString dictionaryForMisspelledRange(const KTextEditor::Range &range) const; void clearMisspellingForWord(const QString &word); public Q_SLOTS: void clearDictionaryRanges(); void setDictionary(const QString &dict, const KTextEditor::Range &range); void revertToDefaultDictionary(const KTextEditor::Range &range); void setDefaultDictionary(const QString &dict); void onTheFlySpellCheckingEnabled(bool enable); void refreshOnTheFlyCheck(const KTextEditor::Range &range = KTextEditor::Range::invalid()); Q_SIGNALS: void dictionaryRangesPresent(bool yesNo); void defaultDictionaryChanged(KTextEditor::DocumentPrivate *document); public: bool containsCharacterEncoding(const KTextEditor::Range &range); typedef QList > OffsetList; int computePositionWrtOffsets(const OffsetList &offsetList, int pos); /** * The first OffsetList is from decoded to encoded, and the second OffsetList from * encoded to decoded. **/ QString decodeCharacters(const KTextEditor::Range &range, KTextEditor::DocumentPrivate::OffsetList &decToEncOffsetList, KTextEditor::DocumentPrivate::OffsetList &encToDecOffsetList); void replaceCharactersByEncoding(const KTextEditor::Range &range); enum EncodedCharaterInsertionPolicy {EncodeAlways, EncodeWhenPresent, EncodeNever}; protected: KateOnTheFlyChecker *m_onTheFlyChecker = nullptr; QString m_defaultDictionary; QList > m_dictionaryRanges; // from KTextEditor::MovingRangeFeedback void rangeInvalid(KTextEditor::MovingRange *movingRange) Q_DECL_OVERRIDE; void rangeEmpty(KTextEditor::MovingRange *movingRange) Q_DECL_OVERRIDE; void deleteDictionaryRange(KTextEditor::MovingRange *movingRange); private: Kate::SwapFile *m_swapfile; public: Kate::SwapFile *swapFile(); //helpers for scripting and codefolding int defStyleNum(int line, int column); bool isComment(int line, int column); public: /** * Find the next modified/saved line, starting at @p startLine. If @p down * is \e true, the search is performed downwards, otherwise upwards. * @return the touched line in the requested search direction, or -1 if not found */ int findTouchedLine(int startLine, bool down); private Q_SLOTS: /** * watch for all started io jobs to remember if file is perhaps loading atm * @param job started job */ void slotStarted(KIO::Job *job); void slotCompleted(); void slotCanceled(); /** * trigger display of loading message, after 1000 ms */ void slotTriggerLoadingMessage(); /** * Abort loading */ void slotAbortLoading(); void slotUrlChanged(const QUrl &url); private: /** * different possible states */ enum DocumentStates { /** * Idle */ DocumentIdle, /** * Loading */ DocumentLoading, /** * Saving */ DocumentSaving, /** * Pre Saving As, this is between ::saveAs is called and ::save */ DocumentPreSavingAs, /** * Saving As */ DocumentSavingAs }; /** * current state */ DocumentStates m_documentState = DocumentIdle; /** * read-write state before loading started */ bool m_readWriteStateBeforeLoading = false; /** * if the document is untitled */ bool m_isUntitled = true; /** * loading job, we want to cancel with cancel in the loading message */ QPointer m_loadingJob; /** * message to show during loading */ QPointer m_loadingMessage; /** * Was there any open error on last file loading? */ bool m_openingError = false; /** * Last open file error message */ QString m_openingErrorMessage; public: /** * reads the line length limit from config, if it is not overriden */ int lineLengthLimit() const; public Q_SLOTS: void openWithLineLengthLimitOverride(); private: /** * timer for delayed handling of mod on hd */ QTimer m_modOnHdTimer; private: /** * currently active template handler; there can be only one */ QPointer m_activeTemplateHandler; private: /** * current autobrace range */ QSharedPointer m_currentAutobraceRange; /** * current autobrace closing charater (e.g. ']') */ QChar m_currentAutobraceClosingChar; private Q_SLOTS: void checkCursorForAutobrace(KTextEditor::View* view, const KTextEditor::Cursor& newPos); public: void setActiveTemplateHandler(KateTemplateHandler* handler); Q_SIGNALS: void loaded(KTextEditor::DocumentPrivate *document); private Q_SLOTS: /** * trigger a close of this document in the application */ void closeDocumentInApplication(); }; #endif diff --git a/src/include/ktexteditor/codecompletionmodel.h b/src/include/ktexteditor/codecompletionmodel.h index 7ee6fe72..163268d4 100644 --- a/src/include/ktexteditor/codecompletionmodel.h +++ b/src/include/ktexteditor/codecompletionmodel.h @@ -1,475 +1,475 @@ /* This file is part of the KDE libraries Copyright (C) 2007-2008 David Nolden Copyright (C) 2005-2006 Hamish Rodda * * 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 KTEXTEDITOR_CODECOMPLETIONMODEL_H #define KTEXTEDITOR_CODECOMPLETIONMODEL_H #include #include #include namespace KTextEditor { class Document; class View; /** * \short An item model for providing code completion, and meta information for * enhanced presentation. * * \section compmodel_intro Introduction * * The CodeCompletionModel is the actual workhorse to provide code completions * in a KTextEditor::View. It is meant to be used in conjunction with the * CodeCompletionInterface. The CodeCompletionModel is not meant to be used as * is. Rather you need to implement a subclass of CodeCompletionModel to actually * generate completions appropriate for your type of Document. * * \section compmodel_implementing Implementing a CodeCompletionModel * * The CodeCompletionModel is a QAbstractItemModel, and can be subclassed in the * same way. It provides default implementations of several members, however, so * in most cases (if your completions are essentially a non-hierarchical, flat list * of matches) you will only need to overload few virtual functions. * * \section compmodel_flatlist Implementing a CodeCompletionModel for a flat list * * For the simple case of a flat list of completions, you will need to: * - Implement completionInvoked() to actually generate/update the list of completion * matches * - implement itemData() (or QAbstractItemModel::data()) to return the information that * should be displayed for each match. * - use setRowCount() to reflect the number of matches. * * \section compmodel_roles_columns Columns and roles * * \todo document the meaning and usage of the columns and roles used by the * CodeCompletionInterface * * \section compmodel_usage Using the new CodeCompletionModel * * To start using your CodeCompletionModel, refer to CodeCompletionInterface. * * \section compmodel_controller ControllerInterface to get more control * * To have more control over code completion implement * CodeCompletionModelControllerInterface in your CodeCompletionModel. * * \see CodeCompletionInterface, CodeCompletionModelControllerInterface * @author Hamish Rodda */ class KTEXTEDITOR_EXPORT CodeCompletionModel : public QAbstractItemModel { Q_OBJECT public: - CodeCompletionModel(QObject *parent); - virtual ~CodeCompletionModel(); + explicit CodeCompletionModel(QObject *parent); + ~CodeCompletionModel() Q_DECL_OVERRIDE; enum Columns { Prefix = 0, /// Icon representing the type of completion. We have a separate icon field /// so that names remain aligned where only some completions have icons, /// and so that they can be rearranged by the user. Icon, Scope, Name, Arguments, Postfix }; static const int ColumnCount = Postfix + 1; enum CompletionProperty { NoProperty = 0x0, FirstProperty = 0x1, // Access specifiers - no more than 1 per item Public = 0x1, Protected = 0x2, Private = 0x4, // Extra access specifiers - any number per item Static = 0x8, Const = 0x10, // Type - no more than 1 per item (except for Template) Namespace = 0x20, Class = 0x40, Struct = 0x80, Union = 0x100, Function = 0x200, Variable = 0x400, Enum = 0x800, Template = 0x1000, TypeAlias = 0x2000, // Special attributes - any number per item Virtual = 0x4000, Override = 0x8000, Inline = 0x10000, Friend = 0x20000, Signal = 0x40000, Slot = 0x80000, // Scope - no more than 1 per item LocalScope = 0x100000, NamespaceScope = 0x200000, GlobalScope = 0x400000, // Keep this in sync so the code knows when to stop LastProperty = GlobalScope }; Q_DECLARE_FLAGS(CompletionProperties, CompletionProperty) enum HighlightMethod { NoHighlighting = 0x0, InternalHighlighting = 0x1, CustomHighlighting = 0x2 }; Q_DECLARE_FLAGS(HighlightMethods, HighlightMethod) /// Meta information is passed through extra {Qt::ItemDataRole}s. /// This information should be returned when requested on the Name column. enum ExtraItemDataRoles { /// The model should return a set of CompletionProperties. CompletionRole = Qt::UserRole, /// The model should return an index to the scope /// -1 represents no scope /// \todo how to sort scope? ScopeIndex, /** * If requested, your model should try to determine whether the * completion in question is a suitable match for the context (ie. * is accessible, exported, + returns the data type required). * * The returned data should ideally be matched against the argument-hint context * set earlier by SetMatchContext. * * Return an integer value that should be positive if the completion is suitable, * and zero if the completion is not suitable. The value should be between 0 an 10, where * 10 means perfect match. * * Return QVariant::Invalid if you are unable to determine this. */ MatchQuality, /** * Is requested before MatchQuality(..) is requested. The item on which this is requested * is an argument-hint item(@see ArgumentHintDepth). When this role is requested, the item should * be noted, and whenever MatchQuality is requested, it should be computed by matching the item given * with MatchQuality into the context chosen by SetMatchContext. * * Feel free to ignore this, but ideally you should return QVariant::Invalid to make clear that your model does not support this. * */ SetMatchContext, /** * Define which highlighting method will be used: * - QVariant::Invalid - allows the editor to choose (usually internal highlighting) * - QVariant::Integer - highlight as specified by HighlightMethods. */ HighlightingMethod, /** * Allows an item to provide custom highlighting. Return a * QList\ in the following format: * - int startColumn (where 0 = start of the completion entry) * - int endColumn (note: not length) * - QTextFormat attribute (note: attribute may be a KTextEditor::Attribute, as it is a child class) * If the attribute is invalid, and the item is an argument-hint, the text will be drawn with * a background-color depending on match-quality, or yellow. * You can use that to mark the actual arguments that are matched in an argument-hint. * * Repeat this triplet as many times as required, however each column must be >= the previous, * and startColumn != endColumn. */ CustomHighlight, /** * Returns the inheritance depth of the completion. For example, a completion * which comes from the base class would have depth 0, one from a parent class * would have depth 1, one from that class' parent 2, etc. you can use this to * symbolize the general distance of a completion-item from a user. It will be used * for sorting. */ InheritanceDepth, /** * This allows items in the completion-list to be expandable. If a model returns an QVariant bool value * that evaluates to true, the completion-widget will draw a handle to expand the item, and will also make * that action accessible through keyboard. */ IsExpandable, /** * After a model returned true for a row on IsExpandable, the row may be expanded by the user. * When this happens, ExpandingWidget is requested. * * The model may return two types of values: * QWidget*: * If the model returns a QVariant of type QWidget*, the code-completion takes over the given widget * and embeds it into the completion-list under the completion-item. The widget will be automatically deleted at some point. * The completion-widget will use the height of the widget as a hint for its preferred size, but it will * resize the widget at will. * QString: * If the mode returns a QVariant of type QString, it will create a small html-widget showing the given html-code, * and embed it into the completion-list under the completion-item. * * Warning: * QWidget* widget; * return QVariant(widget); * Will not work correctly! * Use the following instead.: * QVariant v; * v.setValue(widget); * return v; * * */ ExpandingWidget, /** * Whenever an item is selected, this will be requested from the underlying model. * It may be used as a simple notification that the item was selected. * * Above that, the model may return a QString, which then should then contain html-code. A html-widget * will then be displayed as a one- or two-liner under the currently selected item(it will be partially expanded) * */ ItemSelected, /**Is this completion-item an argument-hint? * The model should return an integral positive number if the item is an argument-hint, and QVariant() or 0 if it is not one. * * The returned depth-integer is important for sorting and matching. * Example: * "otherFunction(function1(function2(" * all functions named function2 should have ArgumentHintDepth 1, all functions found for function1 should have ArgumentHintDepth 2, * and all functions named otherFunction should have ArgumentHintDepth 3 * * Later, a completed item may then be matched with the first argument of function2, the return-type of function2 with the first * argument-type of function1, and the return-type of function1 with the argument-type of otherFunction. * * If the model returns a positive value on this role for a row, the content will be treated specially: * - It will be shown in a separate argument-hint list * - It will be sorted by Argument-hint depth * - Match-qualities will be illustrated by differently highlighting the matched argument if possible * The argument-hint list strings will be built from all source-model, with a little special behavior: * Prefix - Should be all text of the function-signature up to left of the matched argument of the function * Name - Should be the type and name of the function's matched argument. This part will be highlighted differently depending on the match-quality * Suffix - Should be all the text of the function-signature behind the matched argument * * Example: You are matching a function with signature "void test(int param1, int param2)", and you are matching the first argument. * The model should then return: * Prefix: "void test(" * Name: "int param1" * Suffix: ", int param2)" * * If you don't use the highlighting, matching, etc. you can also return the columns in the usual way. * */ ArgumentHintDepth, /** * This will be requested for each item to ask whether it should be included in computing a best-matches list. * If you return a valid positive integer n here, the n best matches will be listed at top of the completion-list separately. * * This is expensive because all items of the whole completion-list will be tested for their matching-quality, with each of the level 1 * argument-hints. * * For that reason the end-user should be able to disable this feature. */ BestMatchesCount, /** * The following three enumeration-values are only used on expanded completion-list items that contain an expanding-widget(@see ExpandingWidget) * * You can use them to allow the user to interact with the widget by keyboard. * * AccessibilityNext will be requested on an item if it is expanded, contains an expanding-widget, and the user triggers a special navigation * short-cut to go to navigate to the next position within the expanding-widget(if applicable). * * Return QVariant(true) if the input was used. * */ AccessibilityNext, /** * AccessibilityPrevious will be requested on an item if it is expanded, contains an expanding-widget, and the user triggers a special navigation * short-cut to go to navigate to the previous position within the expanding-widget(if applicable). * * Return QVariant(true) if the input was used. * */ AccessibilityPrevious, /** * AccessibilityAccept will be requested on an item if it is expanded, contains an expanding-widget, and the user triggers a special * shortcut to trigger the action associated with the position within the expanding-widget the user has navigated to using AccessibilityNext and AccessibilityPrevious. * * This should return QVariant(true) if an action was triggered, else QVariant(false) or QVariant(). * */ AccessibilityAccept, /** * Using this Role, it is possible to greatly optimize the time needed to process very long completion-lists. * * In the completion-list, the items are usually ordered by some properties like argument-hint depth, * inheritance-depth and attributes. Kate usually has to query the completion-models for these values * for each item in the completion-list in order to extract the argument-hints and correctly sort the * completion-list. However, with a very long completion-list, only a very small fraction of the items is actually * visible. * * By using a tree structure you can give the items in a grouped order to kate, so it does not need to look at each * item and query data in order to initially show the completion-list. * * This is how it works: * - You create a tree-structure for your items * - Every inner node of the tree defines one attribute value that all sub-nodes have in common. * - When the inner node is queried for GroupRole, it should return the "ExtraItemDataRoles" that all sub-nodes have in common * - When the inner node is then queried for that exact role, it should return that value. * - No other queries will be done to inner nodes. * - Every leaf node stands for an actual item in the completion list. * * The recommended grouping order is: Argument-hint depth, inheritance depth, attributes. * * This role can also be used to define completely custom groups, bypassing the editors builtin grouping: * - Return Qt::DisplayRole when GroupRole is requested * - Return the lable text of the group when Qt::DisplayRole * - Optional: Return an integer sorting-value when InheritanceDepth is requested. This number will * be used to determine the order of the groups. The order of the builtin groups is: * 1 = Best Matches, 100 = Local Scope, 200 = Public, 300 = Protected, 400 = Private, 500 = Namespace, 600 = Global * You can pick any arbitrary number to position your group relative to these builtin groups. * */ GroupRole, /** * Return a nonzero value here to enforce sorting the item at the end of the list. */ UnimportantItemRole, LastExtraItemDataRole }; void setRowCount(int rowCount); enum InvocationType { AutomaticInvocation, UserInvocation, ManualInvocation }; /** * This function is responsible to generating / updating the list of current * completions. The default implementation does nothing. * * When implementing this function, remember to call setRowCount() (or implement * rowCount()), and to generate the appropriate change notifications (for instance * by calling QAbstractItemModel::reset()). * @param view The view to generate completions for * @param range The range of text to generate completions for * @param invocationType How the code completion was triggered * */ virtual void completionInvoked(KTextEditor::View *view, const KTextEditor::Range &range, InvocationType invocationType); /** * This function is responsible for inserting a selected completion into the * view. The default implementation replaces the text that the completions * were based on with the Qt::DisplayRole of the Name column of the given match. * * @param view the view to insert the completion into * @param word the Range that the completions are based on (what the user entered * so far) * @param index identifies the completion match to insert * */ virtual void executeCompletionItem(KTextEditor::View *view, const Range &word, const QModelIndex &index) const; // Reimplementations /** * Reimplemented from QAbstractItemModel::columnCount(). The default implementation * returns ColumnCount for all indices. * */ int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; /** * Reimplemented from QAbstractItemModel::index(). The default implementation * returns a standard QModelIndex as long as the row and column are valid. * */ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; /** * Reimplemented from QAbstractItemModel::itemData(). The default implementation * returns a map with the QAbstractItemModel::data() for all roles that are used * by the CodeCompletionInterface. You will need to reimplement either this * function or QAbstractItemModel::data() in your CodeCompletionModel. * */ QMap itemData(const QModelIndex &index) const Q_DECL_OVERRIDE; /** * Reimplemented from QAbstractItemModel::parent(). The default implementation * returns an invalid QModelIndex for all items. This is appropriate for * non-hierarchical / flat lists of completions. * */ QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE; /** * Reimplemented from QAbstractItemModel::rowCount(). The default implementation * returns the value set by setRowCount() for invalid (toplevel) indices, and 0 * for all other indices. This is appropriate for non-hierarchical / flat lists * of completions * */ int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE; /** * This function returns true if the model needs grouping, otherwise false. * The default is false if not changed via setHasGroups(). */ bool hasGroups() const; Q_SIGNALS: /** * Emit this if the code-completion for this model was invoked, some time is needed in order to get the data, * and the model is reset once the data is available. * * This only has an effect if emitted from within completionInvoked(..). * * This prevents the code-completion list from showing until this model is reset, * so there is no annoying flashing in the user-interface resulting from other models * supplying their data earlier. * * @note The implementation may choose to show the completion-list anyway after some timeout * * @warning If you emit this, you _must_ also reset the model at some point, * else the code-completion will be completely broken to the user. * Consider that there may always be additional completion-models apart from yours. * * @since 4.3 */ void waitForReset(); /** * Internal */ void hasGroupsChanged(KTextEditor::CodeCompletionModel *model, bool hasGroups); protected: void setHasGroups(bool hasGroups); private: class CodeCompletionModelPrivate *const d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(CodeCompletionModel::CompletionProperties) Q_DECLARE_OPERATORS_FOR_FLAGS(CodeCompletionModel::HighlightMethods) } #endif // KTEXTEDITOR_CODECOMPLETIONMODEL_H diff --git a/src/inputmode/katenormalinputmode.h b/src/inputmode/katenormalinputmode.h index 2dff4df0..8037c6cb 100644 --- a/src/inputmode/katenormalinputmode.h +++ b/src/inputmode/katenormalinputmode.h @@ -1,118 +1,118 @@ /* This file is part of the KDE libraries and the Kate part. * * 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_NORMAL_INPUT_MODE_H__ #define __KATE_NORMAL_INPUT_MODE_H__ #include "kateabstractinputmode.h" class KateNormalInputModeFactory; class KateSearchBar; class KateCommandLineBar; class KateNormalInputMode : public KateAbstractInputMode { KateNormalInputMode(KateViewInternal *viewInternal); friend KateNormalInputModeFactory; public: - virtual ~KateNormalInputMode(); + ~KateNormalInputMode() Q_DECL_OVERRIDE; KTextEditor::View::ViewMode viewMode() const Q_DECL_OVERRIDE; QString viewModeHuman() const Q_DECL_OVERRIDE; KTextEditor::View::InputMode viewInputMode() const Q_DECL_OVERRIDE; QString viewInputModeHuman() const Q_DECL_OVERRIDE; void activate() Q_DECL_OVERRIDE; void deactivate() Q_DECL_OVERRIDE; void reset() Q_DECL_OVERRIDE; bool overwrite() const Q_DECL_OVERRIDE; void overwrittenChar(const QChar &) Q_DECL_OVERRIDE; void clearSelection() Q_DECL_OVERRIDE; bool stealKey(QKeyEvent *) Q_DECL_OVERRIDE; void gotFocus() Q_DECL_OVERRIDE; void lostFocus() Q_DECL_OVERRIDE; void readSessionConfig(const KConfigGroup &config) Q_DECL_OVERRIDE; void writeSessionConfig(KConfigGroup &config) Q_DECL_OVERRIDE; void updateRendererConfig() Q_DECL_OVERRIDE; void updateConfig() Q_DECL_OVERRIDE; void readWriteChanged(bool rw) Q_DECL_OVERRIDE; void find() Q_DECL_OVERRIDE; void findSelectedForwards() Q_DECL_OVERRIDE; void findSelectedBackwards() Q_DECL_OVERRIDE; void findReplace() Q_DECL_OVERRIDE; void findNext() Q_DECL_OVERRIDE; void findPrevious() Q_DECL_OVERRIDE; void activateCommandLine() Q_DECL_OVERRIDE; bool keyPress(QKeyEvent *) Q_DECL_OVERRIDE; bool blinkCaret() const Q_DECL_OVERRIDE; KateRenderer::caretStyles caretStyle() const Q_DECL_OVERRIDE; void toggleInsert() Q_DECL_OVERRIDE; void launchInteractiveCommand(const QString &command) Q_DECL_OVERRIDE; QString bookmarkLabel(int line) const Q_DECL_OVERRIDE; private: /** * Search bar mode: * - Setup Incremental mode, among other things: potential new search pattern * - Setup Power mode, aka find & replace: Also potential new search pattern * - Use current mode and current search pattern or if no Search bar exists, launch Incremental mode */ enum SearchBarMode { IncrementalSearchBar, PowerSearchBar, IncrementalSearchBarOrKeepMode }; /** * Get search bar, create it on demand. (with right mode) * @param mode wanted search bar mode * @return search bar widget */ KateSearchBar *searchBar(const SearchBarMode mode); /** * search bar around? * @return search bar around? */ bool hasSearchBar() const { return m_searchBar; } /** * Get command line bar, create it on demand. * @return command line bar, created if not already there */ KateCommandLineBar *cmdLineBar(); private: KateSearchBar *m_searchBar; KateCommandLineBar *m_cmdLine; }; #endif diff --git a/src/inputmode/katenormalinputmodefactory.h b/src/inputmode/katenormalinputmodefactory.h index 22492389..f19105c4 100644 --- a/src/inputmode/katenormalinputmodefactory.h +++ b/src/inputmode/katenormalinputmodefactory.h @@ -1,38 +1,38 @@ /* This file is part of the KDE libraries and the Kate part. * * 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_NORMAL_INPUT_MODE_FACTORY_H__ #define __KATE_NORMAL_INPUT_MODE_FACTORY_H__ #include "kateabstractinputmodefactory.h" class KateNormalInputModeFactory : public KateAbstractInputModeFactory { public: KateNormalInputModeFactory(); - virtual ~KateNormalInputModeFactory(); + ~KateNormalInputModeFactory() Q_DECL_OVERRIDE; KateAbstractInputMode *createInputMode(KateViewInternal *viewInternal) Q_DECL_OVERRIDE; QString name() Q_DECL_OVERRIDE; KTextEditor::View::InputMode inputMode() Q_DECL_OVERRIDE; KateConfigPage *createConfigPage(QWidget *) Q_DECL_OVERRIDE; }; #endif diff --git a/src/inputmode/kateviinputmode.h b/src/inputmode/kateviinputmode.h index 330148f8..6aa69f38 100644 --- a/src/inputmode/kateviinputmode.h +++ b/src/inputmode/kateviinputmode.h @@ -1,102 +1,102 @@ /* This file is part of the KDE libraries and the Kate part. * * 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_VI_INPUT_MODE_H__ #define __KATE_VI_INPUT_MODE_H__ #include "kateabstractinputmode.h" namespace KateVi { class GlobalState; class InputModeManager; class EmulatedCommandBar; } class KateViInputModeFactory; class KTEXTEDITOR_EXPORT KateViInputMode : public KateAbstractInputMode { explicit KateViInputMode(KateViewInternal *viewInternal, KateVi::GlobalState *global); friend KateViInputModeFactory; public: - virtual ~KateViInputMode(); + ~KateViInputMode() Q_DECL_OVERRIDE; KTextEditor::View::ViewMode viewMode() const Q_DECL_OVERRIDE; QString viewModeHuman() const Q_DECL_OVERRIDE; KTextEditor::View::InputMode viewInputMode() const Q_DECL_OVERRIDE; QString viewInputModeHuman() const Q_DECL_OVERRIDE; void activate() Q_DECL_OVERRIDE; void deactivate() Q_DECL_OVERRIDE; void reset() Q_DECL_OVERRIDE; bool overwrite() const Q_DECL_OVERRIDE; void overwrittenChar(const QChar &) Q_DECL_OVERRIDE; void clearSelection() Q_DECL_OVERRIDE; bool stealKey(QKeyEvent *) Q_DECL_OVERRIDE; void gotFocus() Q_DECL_OVERRIDE; void lostFocus() Q_DECL_OVERRIDE; void readSessionConfig(const KConfigGroup &config) Q_DECL_OVERRIDE; void writeSessionConfig(KConfigGroup &config) Q_DECL_OVERRIDE; void updateRendererConfig() Q_DECL_OVERRIDE; void updateConfig() Q_DECL_OVERRIDE; void readWriteChanged(bool rw) Q_DECL_OVERRIDE; void find() Q_DECL_OVERRIDE; void findSelectedForwards() Q_DECL_OVERRIDE; void findSelectedBackwards() Q_DECL_OVERRIDE; void findReplace() Q_DECL_OVERRIDE; void findNext() Q_DECL_OVERRIDE; void findPrevious() Q_DECL_OVERRIDE; void activateCommandLine() Q_DECL_OVERRIDE; bool keyPress(QKeyEvent *) Q_DECL_OVERRIDE; bool blinkCaret() const Q_DECL_OVERRIDE; KateRenderer::caretStyles caretStyle() const Q_DECL_OVERRIDE; void toggleInsert() Q_DECL_OVERRIDE; void launchInteractiveCommand(const QString &command) Q_DECL_OVERRIDE; QString bookmarkLabel(int line) const Q_DECL_OVERRIDE; public: void showViModeEmulatedCommandBar(); KateVi::EmulatedCommandBar *viModeEmulatedCommandBar(); inline KateVi::GlobalState *globalState() const { return m_viGlobal; } inline KateVi::InputModeManager *viInputModeManager() const { return m_viModeManager; } inline bool isActive() const { return m_activated; } void setCaretStyle(const KateRenderer::caretStyles caret); private: KateVi::InputModeManager *m_viModeManager; KateVi::EmulatedCommandBar *m_viModeEmulatedCommandBar; KateVi::GlobalState *m_viGlobal; KateRenderer::caretStyles m_caret; bool m_nextKeypressIsOverriddenShortCut; // configs bool m_relLineNumbers; bool m_activated; }; #endif diff --git a/src/inputmode/kateviinputmodefactory.h b/src/inputmode/kateviinputmodefactory.h index 8b0bf50c..03539b8d 100644 --- a/src/inputmode/kateviinputmodefactory.h +++ b/src/inputmode/kateviinputmodefactory.h @@ -1,44 +1,44 @@ /* This file is part of the KDE libraries and the Kate part. * * 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_VI_INPUT_MODE_FACTORY_H__ #define __KATE_VI_INPUT_MODE_FACTORY_H__ #include "kateabstractinputmodefactory.h" namespace KateVi { class GlobalState; } class KateViInputMode; class KateViInputModeFactory : public KateAbstractInputModeFactory { friend KateViInputMode; public: KateViInputModeFactory(); - virtual ~KateViInputModeFactory(); + ~KateViInputModeFactory() Q_DECL_OVERRIDE; KateAbstractInputMode *createInputMode(KateViewInternal *viewInternal) Q_DECL_OVERRIDE; QString name() Q_DECL_OVERRIDE; KTextEditor::View::InputMode inputMode() Q_DECL_OVERRIDE; KateConfigPage *createConfigPage(QWidget *) Q_DECL_OVERRIDE; private: KateVi::GlobalState *m_viGlobal; }; #endif diff --git a/src/mode/katemodeconfigpage.h b/src/mode/katemodeconfigpage.h index 2d26b843..b4d4bd72 100644 --- a/src/mode/katemodeconfigpage.h +++ b/src/mode/katemodeconfigpage.h @@ -1,66 +1,66 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2001-2010 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_MODECONFIGPAGE_H__ #define KATE_MODECONFIGPAGE_H__ #include #include #include "katedialogs.h" #include "katemodemanager.h" namespace Ui { class FileTypeConfigWidget; } class ModeConfigPage : public KateConfigPage { Q_OBJECT public: explicit ModeConfigPage(QWidget *parent); - ~ModeConfigPage(); + ~ModeConfigPage() Q_DECL_OVERRIDE; QString name() const Q_DECL_OVERRIDE; public Q_SLOTS: void apply() Q_DECL_OVERRIDE; void reload() Q_DECL_OVERRIDE; void reset() Q_DECL_OVERRIDE; void defaults() Q_DECL_OVERRIDE; private Q_SLOTS: void update(); void deleteType(); void newType(); void typeChanged(int type); void showMTDlg(); void save(); void hlDownload(); private: Ui::FileTypeConfigWidget *ui; QList m_types; int m_lastType; }; #endif diff --git a/src/render/katerenderrange.h b/src/render/katerenderrange.h index 34613921..432fe8da 100644 --- a/src/render/katerenderrange.h +++ b/src/render/katerenderrange.h @@ -1,75 +1,75 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2003-2005 Hamish Rodda * Copyright (C) 2008 David Nolden * * 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 KATERENDERRANGE_H #define KATERENDERRANGE_H #include #include #include #include class KateRenderRange { public: virtual ~KateRenderRange() {} virtual KTextEditor::Cursor nextBoundary() const = 0; virtual bool advanceTo(const KTextEditor::Cursor &pos) = 0; virtual KTextEditor::Attribute::Ptr currentAttribute() const = 0; virtual bool isReady() const; }; typedef QPair pairRA; class NormalRenderRange : public KateRenderRange { public: NormalRenderRange(); - virtual ~NormalRenderRange(); + ~NormalRenderRange() Q_DECL_OVERRIDE; void addRange(KTextEditor::Range *range, KTextEditor::Attribute::Ptr attribute); KTextEditor::Cursor nextBoundary() const Q_DECL_OVERRIDE; bool advanceTo(const KTextEditor::Cursor &pos) Q_DECL_OVERRIDE; KTextEditor::Attribute::Ptr currentAttribute() const Q_DECL_OVERRIDE; private: QList m_ranges; KTextEditor::Cursor m_nextBoundary; KTextEditor::Attribute::Ptr m_currentAttribute; int m_currentRange = 0; }; class RenderRangeList : public QList { public: ~RenderRangeList(); KTextEditor::Cursor nextBoundary() const; void advanceTo(const KTextEditor::Cursor &pos); bool hasAttribute() const; KTextEditor::Attribute::Ptr generateAttribute() const; private: KTextEditor::Cursor m_currentPos; }; #endif diff --git a/src/schema/katestyletreewidget.cpp b/src/schema/katestyletreewidget.cpp index 699e6dda..55e7e4bd 100644 --- a/src/schema/katestyletreewidget.cpp +++ b/src/schema/katestyletreewidget.cpp @@ -1,739 +1,739 @@ /* This file is part of the KDE libraries Copyright (C) 2001-2003 Christoph Cullmann Copyright (C) 2002, 2003 Anders Lund Copyright (C) 2005-2006 Hamish Rodda Copyright (C) 2007 Mirko Stocker * * 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. */ #include "katestyletreewidget.h" #include "kateconfig.h" #include "kateextendedattribute.h" #include "katedefaultcolors.h" #include "kateglobal.h" #include #include #include #include #include #include #include #include #include //BEGIN KateStyleTreeDelegate class KateStyleTreeDelegate : public QStyledItemDelegate { public: KateStyleTreeDelegate(KateStyleTreeWidget *widget); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE; private: QBrush getBrushForColorColumn(const QModelIndex &index, int column) const; KateStyleTreeWidget *m_widget; }; //END //BEGIN KateStyleTreeWidgetItem decl /* QListViewItem subclass to display/edit a style, bold/italic is check boxes, normal and selected colors are boxes, which will display a color chooser when activated. The context name for the style will be drawn using the editor default font and the chosen colors. This widget id designed to handle the default as well as the individual hl style lists. This widget is designed to work with the KateStyleTreeWidget class exclusively. Added by anders, jan 23 2002. */ class KateStyleTreeWidgetItem : public QTreeWidgetItem { public: KateStyleTreeWidgetItem(QTreeWidgetItem *parent, const QString &styleName, KTextEditor::Attribute::Ptr defaultstyle, KTextEditor::Attribute::Ptr data = KTextEditor::Attribute::Ptr()); KateStyleTreeWidgetItem(QTreeWidget *parent, const QString &styleName, KTextEditor::Attribute::Ptr defaultstyle, KTextEditor::Attribute::Ptr data = KTextEditor::Attribute::Ptr()); - ~KateStyleTreeWidgetItem() {} + ~KateStyleTreeWidgetItem() Q_DECL_OVERRIDE {} enum columns { Context = 0, Bold, Italic, Underline, StrikeOut, Foreground, SelectedForeground, Background, SelectedBackground, UseDefaultStyle, NumColumns }; /* initializes the style from the default and the hldata */ void initStyle(); /* updates the hldata's style */ void updateStyle(); /* For bool fields, toggles them, for color fields, display a color chooser */ void changeProperty(int p); /** unset a color. * c is 100 (BGColor) or 101 (SelectedBGColor) for now. */ void unsetColor(int c); /* style context name */ QString contextName() const { return text(0); } /* only true for a hl mode item using its default style */ bool defStyle() const; /* true for default styles */ bool isDefault() const; /* whichever style is active (currentStyle for hl mode styles not using the default style, defaultStyle otherwise) */ KTextEditor::Attribute::Ptr style() const { return currentStyle; } QVariant data(int column, int role) const Q_DECL_OVERRIDE; KateStyleTreeWidget *treeWidget() const; private: /* private methods to change properties */ void toggleDefStyle(); void setColor(int); /* helper function to copy the default style into the KateExtendedAttribute, when a property is changed and we are using default style. */ KTextEditor::Attribute::Ptr currentStyle, // the style currently in use (was "is") defaultStyle; // default style for hl mode contexts and default styles (was "ds") KTextEditor::Attribute::Ptr actualStyle; // itemdata for hl mode contexts (was "st") }; //END //BEGIN KateStyleTreeWidget KateStyleTreeWidget::KateStyleTreeWidget(QWidget *parent, bool showUseDefaults) : QTreeWidget(parent) { setItemDelegate(new KateStyleTreeDelegate(this)); setRootIsDecorated(false); QStringList headers; headers << i18nc("@title:column Meaning of text in editor", "Context") << QString() << QString() << QString() << QString() << i18nc("@title:column Text style", "Normal") << i18nc("@title:column Text style", "Selected") << i18nc("@title:column Text style", "Background") << i18nc("@title:column Text style", "Background Selected"); if (showUseDefaults) { headers << i18n("Use Default Style"); } setHeaderLabels(headers); headerItem()->setIcon(1, QIcon::fromTheme(QStringLiteral("format-text-bold"))); headerItem()->setIcon(2, QIcon::fromTheme(QStringLiteral("format-text-italic"))); headerItem()->setIcon(3, QIcon::fromTheme(QStringLiteral("format-text-underline"))); headerItem()->setIcon(4, QIcon::fromTheme(QStringLiteral("format-text-strikethrough"))); // grap the bg color, selected color and default font const KColorScheme &colors(KTextEditor::EditorPrivate::self()->defaultColors().view()); normalcol = colors.foreground().color(); bgcol = KateRendererConfig::global()->backgroundColor(); selcol = KateRendererConfig::global()->selectionColor(); docfont = KateRendererConfig::global()->font(); QPalette pal = viewport()->palette(); pal.setColor(QPalette::Background, bgcol); viewport()->setPalette(pal); } QIcon brushIcon(const QColor &color) { QPixmap pm(16, 16); QRect all(0, 0, 15, 15); { QPainter p(&pm); p.fillRect(all, color); p.setPen(Qt::black); p.drawRect(all); } return QIcon(pm); } bool KateStyleTreeWidget::edit(const QModelIndex &index, EditTrigger trigger, QEvent *event) { if (index.column() == KateStyleTreeWidgetItem::Context) { return false; } KateStyleTreeWidgetItem *i = dynamic_cast(itemFromIndex(index)); if (!i) { return QTreeWidget::edit(index, trigger, event); } switch (trigger) { case QAbstractItemView::DoubleClicked: case QAbstractItemView::SelectedClicked: case QAbstractItemView::EditKeyPressed: i->changeProperty(index.column()); update(index); update(index.sibling(index.row(), KateStyleTreeWidgetItem::Context)); return false; default: return QTreeWidget::edit(index, trigger, event); } } void KateStyleTreeWidget::resizeColumns() { for (int i = 0; i < columnCount(); ++i) { resizeColumnToContents(i); } } void KateStyleTreeWidget::showEvent(QShowEvent *event) { QTreeWidget::showEvent(event); resizeColumns(); } void KateStyleTreeWidget::contextMenuEvent(QContextMenuEvent *event) { KateStyleTreeWidgetItem *i = dynamic_cast(itemAt(event->pos())); if (!i) { return; } QMenu m(this); KTextEditor::Attribute::Ptr currentStyle = i->style(); // the title is used, because the menu obscures the context name when // displayed on behalf of spacePressed(). QPainter p; p.setPen(Qt::black); const QIcon emptyColorIcon = brushIcon(viewport()->palette().base().color()); QIcon cl = brushIcon(i->style()->foreground().color()); QIcon scl = brushIcon(i->style()->selectedForeground().color()); QIcon bgcl = i->style()->hasProperty(QTextFormat::BackgroundBrush) ? brushIcon(i->style()->background().color()) : emptyColorIcon; QIcon sbgcl = i->style()->hasProperty(CustomProperties::SelectedBackground) ? brushIcon(i->style()->selectedBackground().color()) : emptyColorIcon; m.addSection(i->contextName()); QAction *a = m.addAction(i18n("&Bold"), this, SLOT(changeProperty())); a->setCheckable(true); a->setChecked(currentStyle->fontBold()); a->setData(KateStyleTreeWidgetItem::Bold); a = m.addAction(i18n("&Italic"), this, SLOT(changeProperty())); a->setCheckable(true); a->setChecked(currentStyle->fontItalic()); a->setData(KateStyleTreeWidgetItem::Italic); a = m.addAction(i18n("&Underline"), this, SLOT(changeProperty())); a->setCheckable(true); a->setChecked(currentStyle->fontUnderline()); a->setData(KateStyleTreeWidgetItem::Underline); a = m.addAction(i18n("S&trikeout"), this, SLOT(changeProperty())); a->setCheckable(true); a->setChecked(currentStyle->fontStrikeOut()); a->setData(KateStyleTreeWidgetItem::StrikeOut); m.addSeparator(); a = m.addAction(cl, i18n("Normal &Color..."), this, SLOT(changeProperty())); a->setData(KateStyleTreeWidgetItem::Foreground); a = m.addAction(scl, i18n("&Selected Color..."), this, SLOT(changeProperty())); a->setData(KateStyleTreeWidgetItem::SelectedForeground); a = m.addAction(bgcl, i18n("&Background Color..."), this, SLOT(changeProperty())); a->setData(KateStyleTreeWidgetItem::Background); a = m.addAction(sbgcl, i18n("S&elected Background Color..."), this, SLOT(changeProperty())); a->setData(KateStyleTreeWidgetItem::SelectedBackground); // defaulters m.addSeparator(); a = m.addAction(emptyColorIcon, i18n("Unset Normal Color"), this, SLOT(unsetColor())); a->setData(1); a = m.addAction(emptyColorIcon, i18n("Unset Selected Color"), this, SLOT(unsetColor())); a->setData(2); // unsetters KTextEditor::Attribute::Ptr style = i->style(); if (style->hasProperty(QTextFormat::BackgroundBrush)) { a = m.addAction(emptyColorIcon, i18n("Unset Background Color"), this, SLOT(unsetColor())); a->setData(3); } if (style->hasProperty(CustomProperties::SelectedBackground)) { a = m.addAction(emptyColorIcon, i18n("Unset Selected Background Color"), this, SLOT(unsetColor())); a->setData(4); } if (! i->isDefault() && ! i->defStyle()) { m.addSeparator(); a = m.addAction(i18n("Use &Default Style"), this, SLOT(changeProperty())); a->setCheckable(true); a->setChecked(i->defStyle()); a->setData(KateStyleTreeWidgetItem::UseDefaultStyle); } m.exec(event->globalPos()); } void KateStyleTreeWidget::changeProperty() { static_cast(currentItem())->changeProperty(static_cast(sender())->data().toInt()); } void KateStyleTreeWidget::unsetColor() { static_cast(currentItem())->unsetColor(static_cast(sender())->data().toInt()); } void KateStyleTreeWidget::updateGroupHeadings() { for (int i = 0; i < topLevelItemCount(); i++) { QTreeWidgetItem *currentTopLevelItem = topLevelItem(i); QTreeWidgetItem *firstChild = currentTopLevelItem->child(0); if (firstChild) { QColor foregroundColor = firstChild->data(KateStyleTreeWidgetItem::Foreground, Qt::DisplayRole).value(); QColor backgroundColor = firstChild->data(KateStyleTreeWidgetItem::Background, Qt::DisplayRole).value(); currentTopLevelItem->setForeground(KateStyleTreeWidgetItem::Context, foregroundColor); if (backgroundColor.isValid()) { currentTopLevelItem->setBackground(KateStyleTreeWidgetItem::Context, backgroundColor); } } } } void KateStyleTreeWidget::emitChanged() { updateGroupHeadings(); emit changed(); } void KateStyleTreeWidget::addItem(const QString &styleName, KTextEditor::Attribute::Ptr defaultstyle, KTextEditor::Attribute::Ptr data) { new KateStyleTreeWidgetItem(this, styleName, defaultstyle, data); } void KateStyleTreeWidget::addItem(QTreeWidgetItem *parent, const QString &styleName, KTextEditor::Attribute::Ptr defaultstyle, KTextEditor::Attribute::Ptr data) { new KateStyleTreeWidgetItem(parent, styleName, defaultstyle, data); updateGroupHeadings(); } //END //BEGIN KateStyleTreeWidgetItem KateStyleTreeDelegate::KateStyleTreeDelegate(KateStyleTreeWidget *widget) : m_widget(widget) { } QBrush KateStyleTreeDelegate::getBrushForColorColumn(const QModelIndex &index, int column) const { QModelIndex colorIndex = index.sibling(index.row(), column); QVariant displayData = colorIndex.model()->data(colorIndex); return displayData.value(); } void KateStyleTreeDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { static QSet columns; if (columns.isEmpty()) { columns << KateStyleTreeWidgetItem::Foreground << KateStyleTreeWidgetItem::SelectedForeground << KateStyleTreeWidgetItem::Background << KateStyleTreeWidgetItem::SelectedBackground; } if (index.column() == KateStyleTreeWidgetItem::Context) { QStyleOptionViewItem styleContextItem(option); QBrush brush = getBrushForColorColumn(index, KateStyleTreeWidgetItem::SelectedBackground); if (brush != QBrush()) { styleContextItem.palette.setBrush(QPalette::Highlight, brush); } brush = getBrushForColorColumn(index, KateStyleTreeWidgetItem::SelectedForeground); if (brush != QBrush()) { styleContextItem.palette.setBrush(QPalette::HighlightedText, brush); } return QStyledItemDelegate::paint(painter, styleContextItem, index); } QStyledItemDelegate::paint(painter, option, index); if (!columns.contains(index.column())) { return; } QVariant displayData = index.model()->data(index); if (displayData.type() != QVariant::Brush) { return; } QBrush brush = displayData.value(); QStyleOptionButton opt; opt.rect = option.rect; opt.palette = m_widget->palette(); bool set = brush != QBrush(); if (!set) { opt.text = i18nc("No text or background color set", "None set"); brush = Qt::white; } m_widget->style()->drawControl(QStyle::CE_PushButton, &opt, painter, m_widget); if (set) { painter->fillRect(m_widget->style()->subElementRect(QStyle::SE_PushButtonContents, &opt, m_widget), brush); } } KateStyleTreeWidgetItem::KateStyleTreeWidgetItem(QTreeWidgetItem *parent, const QString &stylename, KTextEditor::Attribute::Ptr defaultAttribute, KTextEditor::Attribute::Ptr actualAttribute) : QTreeWidgetItem(parent), currentStyle(nullptr), defaultStyle(defaultAttribute), actualStyle(actualAttribute) { initStyle(); setText(0, stylename); } KateStyleTreeWidgetItem::KateStyleTreeWidgetItem(QTreeWidget *parent, const QString &stylename, KTextEditor::Attribute::Ptr defaultAttribute, KTextEditor::Attribute::Ptr actualAttribute) : QTreeWidgetItem(parent), currentStyle(nullptr), defaultStyle(defaultAttribute), actualStyle(actualAttribute) { initStyle(); setText(0, stylename); } void KateStyleTreeWidgetItem::initStyle() { if (!actualStyle) { currentStyle = defaultStyle; } else { currentStyle = new KTextEditor::Attribute(*defaultStyle); if (actualStyle->hasAnyProperty()) { *currentStyle += *actualStyle; } } setFlags(Qt::ItemIsSelectable | Qt::ItemIsEditable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); } static Qt::CheckState toCheckState(bool b) { return b ? Qt::Checked : Qt::Unchecked; } QVariant KateStyleTreeWidgetItem::data(int column, int role) const { if (column == Context) { switch (role) { case Qt::ForegroundRole: if (style()->hasProperty(QTextFormat::ForegroundBrush)) { return style()->foreground().color(); } break; case Qt::BackgroundRole: if (style()->hasProperty(QTextFormat::BackgroundBrush)) { return style()->background().color(); } break; case Qt::FontRole: return style()->font(); break; } } if (role == Qt::CheckStateRole) { switch (column) { case Bold: return toCheckState(style()->fontBold()); case Italic: return toCheckState(style()->fontItalic()); case Underline: return toCheckState(style()->fontUnderline()); case StrikeOut: return toCheckState(style()->fontStrikeOut()); case UseDefaultStyle: /* can't compare all attributes, currentStyle has always more than defaultStyle (e.g. the item's name), * so we just compare the important ones:*/ return toCheckState( currentStyle->foreground() == defaultStyle->foreground() && currentStyle->background() == defaultStyle->background() && currentStyle->selectedForeground() == defaultStyle->selectedForeground() && currentStyle->selectedBackground() == defaultStyle->selectedBackground() && currentStyle->fontBold() == defaultStyle->fontBold() && currentStyle->fontItalic() == defaultStyle->fontItalic() && currentStyle->fontUnderline() == defaultStyle->fontUnderline() && currentStyle->fontStrikeOut() == defaultStyle->fontStrikeOut()); } } if (role == Qt::DisplayRole) { switch (column) { case Foreground: return style()->foreground(); case SelectedForeground: return style()->selectedForeground(); case Background: return style()->background(); case SelectedBackground: return style()->selectedBackground(); } } return QTreeWidgetItem::data(column, role); } void KateStyleTreeWidgetItem::updateStyle() { // nothing there, not update it, will crash if (!actualStyle) { return; } if (currentStyle->hasProperty(QTextFormat::FontWeight)) { if (currentStyle->fontWeight() != actualStyle->fontWeight()) { actualStyle->setFontWeight(currentStyle->fontWeight()); } } else { actualStyle->clearProperty(QTextFormat::FontWeight); } if (currentStyle->hasProperty(QTextFormat::FontItalic)) { if (currentStyle->fontItalic() != actualStyle->fontItalic()) { actualStyle->setFontItalic(currentStyle->fontItalic()); } } else { actualStyle->clearProperty(QTextFormat::FontItalic); } if (currentStyle->hasProperty(QTextFormat::FontStrikeOut)) { if (currentStyle->fontStrikeOut() != actualStyle->fontStrikeOut()) { actualStyle->setFontStrikeOut(currentStyle->fontStrikeOut()); } } else { actualStyle->clearProperty(QTextFormat::FontStrikeOut); } if (currentStyle->hasProperty(QTextFormat::FontUnderline)) { if (currentStyle->fontUnderline() != actualStyle->fontUnderline()) { actualStyle->setFontUnderline(currentStyle->fontUnderline()); } } else { actualStyle->clearProperty(QTextFormat::FontUnderline); } if (currentStyle->hasProperty(CustomProperties::Outline)) { if (currentStyle->outline() != actualStyle->outline()) { actualStyle->setOutline(currentStyle->outline()); } } else { actualStyle->clearProperty(CustomProperties::Outline); } if (currentStyle->hasProperty(QTextFormat::ForegroundBrush)) { if (currentStyle->foreground() != actualStyle->foreground()) { actualStyle->setForeground(currentStyle->foreground()); } } else { actualStyle->clearProperty(QTextFormat::ForegroundBrush); } if (currentStyle->hasProperty(CustomProperties::SelectedForeground)) { if (currentStyle->selectedForeground() != actualStyle->selectedForeground()) { actualStyle->setSelectedForeground(currentStyle->selectedForeground()); } } else { actualStyle->clearProperty(CustomProperties::SelectedForeground); } if (currentStyle->hasProperty(QTextFormat::BackgroundBrush)) { if (currentStyle->background() != actualStyle->background()) { actualStyle->setBackground(currentStyle->background()); } } else { actualStyle->clearProperty(QTextFormat::BackgroundBrush); } if (currentStyle->hasProperty(CustomProperties::SelectedBackground)) { if (currentStyle->selectedBackground() != actualStyle->selectedBackground()) { actualStyle->setSelectedBackground(currentStyle->selectedBackground()); } } else { actualStyle->clearProperty(CustomProperties::SelectedBackground); } } /* only true for a hl mode item using its default style */ bool KateStyleTreeWidgetItem::defStyle() const { return actualStyle && actualStyle->properties() != defaultStyle->properties(); } /* true for default styles */ bool KateStyleTreeWidgetItem::isDefault() const { return actualStyle ? false : true; } void KateStyleTreeWidgetItem::changeProperty(int p) { if (p == Bold) { currentStyle->setFontBold(! currentStyle->fontBold()); } else if (p == Italic) { currentStyle->setFontItalic(! currentStyle->fontItalic()); } else if (p == Underline) { currentStyle->setFontUnderline(! currentStyle->fontUnderline()); } else if (p == StrikeOut) { currentStyle->setFontStrikeOut(! currentStyle->fontStrikeOut()); } else if (p == UseDefaultStyle) { toggleDefStyle(); } else { setColor(p); } updateStyle(); treeWidget()->emitChanged(); } void KateStyleTreeWidgetItem::toggleDefStyle() { if (*currentStyle == *defaultStyle) { KMessageBox::information(treeWidget(), i18n("\"Use Default Style\" will be automatically unset when you change any style properties."), i18n("Kate Styles"), QStringLiteral("Kate hl config use defaults")); } else { currentStyle = KTextEditor::Attribute::Ptr(new KTextEditor::Attribute(*defaultStyle)); updateStyle(); QModelIndex currentIndex = treeWidget()->currentIndex(); while (currentIndex.isValid()) { treeWidget()->update(currentIndex); currentIndex = currentIndex.sibling(currentIndex.row(), currentIndex.column() - 1); } } } void KateStyleTreeWidgetItem::setColor(int column) { QColor c; // use this QColor d; // default color if (column == Foreground) { c = currentStyle->foreground().color(); d = defaultStyle->foreground().color(); } else if (column == SelectedForeground) { c = currentStyle->selectedForeground().color(); d = defaultStyle->selectedForeground().color(); } else if (column == Background) { c = currentStyle->background().color(); d = defaultStyle->background().color(); } else if (column == SelectedBackground) { c = currentStyle->selectedBackground().color(); d = defaultStyle->selectedBackground().color(); } if (!c.isValid()) { c = d; } const QColor selectedColor = QColorDialog::getColor(c, treeWidget()); if (!selectedColor.isValid()) { return; } // if set default, and the attrib is set in the default style use it // else if set default, unset it // else set the selected color switch (column) { case Foreground: currentStyle->setForeground(selectedColor); break; case SelectedForeground: currentStyle->setSelectedForeground(selectedColor); break; case Background: currentStyle->setBackground(selectedColor); break; case SelectedBackground: currentStyle->setSelectedBackground(selectedColor); break; } //FIXME //repaint(); } void KateStyleTreeWidgetItem::unsetColor(int colorId) { switch (colorId) { case 1: if (defaultStyle->hasProperty(QTextFormat::ForegroundBrush)) { currentStyle->setForeground(defaultStyle->foreground()); } else { currentStyle->clearProperty(QTextFormat::ForegroundBrush); } break; case 2: if (defaultStyle->hasProperty(CustomProperties::SelectedForeground)) { currentStyle->setSelectedForeground(defaultStyle->selectedForeground()); } else { currentStyle->clearProperty(CustomProperties::SelectedForeground); } break; case 3: if (currentStyle->hasProperty(QTextFormat::BackgroundBrush)) { currentStyle->clearProperty(QTextFormat::BackgroundBrush); } break; case 4: if (currentStyle->hasProperty(CustomProperties::SelectedBackground)) { currentStyle->clearProperty(CustomProperties::SelectedBackground); } break; } updateStyle(); treeWidget()->emitChanged(); } KateStyleTreeWidget *KateStyleTreeWidgetItem::treeWidget() const { return static_cast(QTreeWidgetItem::treeWidget()); } //END diff --git a/src/script/katecommandlinescript.h b/src/script/katecommandlinescript.h index c432ee7f..0e98e176 100644 --- a/src/script/katecommandlinescript.h +++ b/src/script/katecommandlinescript.h @@ -1,86 +1,86 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2009 Dominik Haumann * * 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_COMMANDLINE_SCRIPT_H #define KATE_COMMANDLINE_SCRIPT_H #include "katescript.h" #include "kateview.h" #include #include class KateCommandLineScriptHeader { public: KateCommandLineScriptHeader() {} inline void setFunctions(const QStringList &functions) { m_functions = functions; } inline const QStringList &functions() const { return m_functions; } inline void setActions(const QJsonArray &actions) { m_actions = actions; } inline const QJsonArray &actions() const { return m_actions; } private: QStringList m_functions; ///< the functions the script contains QJsonArray m_actions; ///< the action for this script }; /** * A specialized class for scripts that are of type * KateScriptInformation::IndentationScript */ class KateCommandLineScript : public KateScript, public KTextEditor::Command { public: KateCommandLineScript(const QString &url, const KateCommandLineScriptHeader &header); - virtual ~KateCommandLineScript(); + ~KateCommandLineScript() Q_DECL_OVERRIDE; const KateCommandLineScriptHeader &commandHeader(); bool callFunction(const QString &cmd, const QStringList args, QString &errorMessage); // // KTextEditor::Command interface // public: bool help(KTextEditor::View *view, const QString &cmd, QString &msg) Q_DECL_OVERRIDE; bool exec(KTextEditor::View *view, const QString &cmd, QString &msg, const KTextEditor::Range &range = KTextEditor::Range::invalid()) Q_DECL_OVERRIDE; bool supportsRange(const QString &cmd) Q_DECL_OVERRIDE; private: KateCommandLineScriptHeader m_commandHeader; }; #endif diff --git a/src/script/katescriptmanager.h b/src/script/katescriptmanager.h index d7df0e0e..f2724ba0 100644 --- a/src/script/katescriptmanager.h +++ b/src/script/katescriptmanager.h @@ -1,140 +1,140 @@ // This file is part of the KDE libraries // Copyright (C) 2008 Paul Giannaros // Copyright (C) 2009 Dominik Haumann // Copyright (C) 2010 Joseph Wenninger // // 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) version 3. // // 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_SCRIPT_MANAGER_H #define KATE_SCRIPT_MANAGER_H #include #include #include #include "katescript.h" #include "kateindentscript.h" #include "katecommandlinescript.h" class QString; /** * Manage the scripts on disks -- find them and query them. * Provides access to loaded scripts too. */ class KateScriptManager : public KTextEditor::Command { Q_OBJECT KateScriptManager(); static KateScriptManager *m_instance; public: - virtual ~KateScriptManager(); + ~KateScriptManager() Q_DECL_OVERRIDE; /** Get all scripts available in the command line */ const QVector &commandLineScripts() { return m_commandLineScripts; } /** * Get an indentation script for the given language -- if there is more than * one result, this will return the script with the highest priority. If * both have the same priority, an arbitrary script will be returned. * If no scripts are found, 0 is returned. */ KateIndentScript *indenter(const QString &language); // // KTextEditor::Command // public: /** * execute command * @param view view to use for execution * @param cmd cmd string * @param errorMsg error to return if no success * @return success */ bool exec(KTextEditor::View *view, const QString &cmd, QString &errorMsg, const KTextEditor::Range &) Q_DECL_OVERRIDE; /** * get help for a command * @param view view to use * @param cmd cmd name * @param msg help message * @return help available or not */ bool help(KTextEditor::View *view, const QString &cmd, QString &msg) Q_DECL_OVERRIDE; static KateScriptManager *self() { if (m_instance == nullptr) { m_instance = new KateScriptManager(); } return m_instance; } // // Helper methods // public: /** * Collect all scripts. */ void collect(); public: KateIndentScript *indentationScript(const QString &scriptname) { return m_indentationScriptMap.value(scriptname); } int indentationScriptCount() { return m_indentationScripts.size(); } KateIndentScript *indentationScriptByIndex(int index) { return m_indentationScripts[index]; } public: /** explicitly reload all scripts */ void reload(); Q_SIGNALS: /** this signal is emitted when all scripts are _deleted_ and reloaded again. */ void reloaded(); private: /** List of all command line scripts */ QVector m_commandLineScripts; /** list of all indentation scripts */ QList m_indentationScripts; /** hash of all existing indenter scripts, hashes basename -> script */ QHash m_indentationScriptMap; /** Map of language to indent scripts */ QHash > m_languageToIndenters; }; #endif diff --git a/src/spellcheck/ontheflycheck.h b/src/spellcheck/ontheflycheck.h index dd2ab1ae..4bd38494 100644 --- a/src/spellcheck/ontheflycheck.h +++ b/src/spellcheck/ontheflycheck.h @@ -1,141 +1,141 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2008-2010 by Michel Ludwig * Copyright (C) 2009 by Joseph Wenninger * * 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 ONTHEFLYCHECK_H #define ONTHEFLYCHECK_H #include #include #include #include #include #include #include #include "katedocument.h" namespace Sonnet { class BackgroundChecker; } class KateOnTheFlyChecker : public QObject, private KTextEditor::MovingRangeFeedback { Q_OBJECT enum ModificationType {TEXT_INSERTED = 0, TEXT_REMOVED}; typedef QPair SpellCheckItem; typedef QList MovingRangeList; typedef QPair MisspelledItem; typedef QList MisspelledList; typedef QPair ModificationItem; typedef QList ModificationList; public: - KateOnTheFlyChecker(KTextEditor::DocumentPrivate *document); - ~KateOnTheFlyChecker(); + explicit KateOnTheFlyChecker(KTextEditor::DocumentPrivate *document); + ~KateOnTheFlyChecker() Q_DECL_OVERRIDE; QPair getMisspelledItem(const KTextEditor::Cursor &cursor) const; QString dictionaryForMisspelledRange(const KTextEditor::Range &range) const; void clearMisspellingForWord(const QString &word); public Q_SLOTS: void textInserted(KTextEditor::Document *document, const KTextEditor::Range &range); void textRemoved(KTextEditor::Document *document, const KTextEditor::Range &range); void updateConfig(); void refreshSpellCheck(const KTextEditor::Range &range = KTextEditor::Range::invalid()); void updateInstalledMovingRanges(KTextEditor::ViewPrivate *view); protected: KTextEditor::DocumentPrivate *const m_document; Sonnet::Speller m_speller; QList m_spellCheckQueue; Sonnet::BackgroundChecker *m_backgroundChecker; SpellCheckItem m_currentlyCheckedItem; MisspelledList m_misspelledList; ModificationList m_modificationList; KTextEditor::DocumentPrivate::OffsetList m_currentDecToEncOffsetList; QMap m_displayRangeMap; void freeDocument(); MovingRangeList installedMovingRanges(const KTextEditor::Range &range); void queueLineSpellCheck(KTextEditor::DocumentPrivate *document, int line); /** * 'range' must be on a single line **/ void queueLineSpellCheck(const KTextEditor::Range &range, const QString &dictionary); void queueSpellCheckVisibleRange(const KTextEditor::Range &range); void queueSpellCheckVisibleRange(KTextEditor::ViewPrivate *view, const KTextEditor::Range &range); void addToSpellCheckQueue(const KTextEditor::Range &range, const QString &dictionary); void addToSpellCheckQueue(KTextEditor::MovingRange *range, const QString &dictionary); QTimer *m_viewRefreshTimer; QPointer m_refreshView; virtual void removeRangeFromEverything(KTextEditor::MovingRange *range); bool removeRangeFromCurrentSpellCheck(KTextEditor::MovingRange *range); bool removeRangeFromSpellCheckQueue(KTextEditor::MovingRange *range); void rangeEmpty(KTextEditor::MovingRange *range) Q_DECL_OVERRIDE; void rangeInvalid(KTextEditor::MovingRange *range) Q_DECL_OVERRIDE; void mouseEnteredRange(KTextEditor::MovingRange *range, KTextEditor::View *view) Q_DECL_OVERRIDE; void mouseExitedRange(KTextEditor::MovingRange *range, KTextEditor::View *view) Q_DECL_OVERRIDE; void caretEnteredRange(KTextEditor::MovingRange *range, KTextEditor::View *view) Q_DECL_OVERRIDE; void caretExitedRange(KTextEditor::MovingRange *range, KTextEditor::View *view) Q_DECL_OVERRIDE; KTextEditor::Range findWordBoundaries(const KTextEditor::Cursor &begin, const KTextEditor::Cursor &end); void deleteMovingRange(KTextEditor::MovingRange *range); void deleteMovingRanges(const QList &list); void deleteMovingRangeQuickly(KTextEditor::MovingRange *range); void stopCurrentSpellCheck(); protected Q_SLOTS: void performSpellCheck(); void misspelling(const QString &word, int start); void spellCheckDone(); void viewDestroyed(QObject *obj); void addView(KTextEditor::Document *document, KTextEditor::View *view); void removeView(KTextEditor::View *view); void restartViewRefreshTimer(KTextEditor::ViewPrivate *view); void viewRefreshTimeout(); void handleModifiedRanges(); void handleInsertedText(const KTextEditor::Range &range); void handleRemovedText(const KTextEditor::Range &range); void handleRespellCheckBlock(int start, int end); bool removeRangeFromModificationList(KTextEditor::MovingRange *range); void clearModificationList(); }; #endif diff --git a/src/spellcheck/spellcheckbar.h b/src/spellcheck/spellcheckbar.h index 375fd8dc..8fdf409c 100644 --- a/src/spellcheck/spellcheckbar.h +++ b/src/spellcheck/spellcheckbar.h @@ -1,160 +1,160 @@ /* * Copyright (C) 2003 Zack Rusin * Copyright (C) 2009-2010 Michel Ludwig * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA */ #ifndef SONNET_DIALOG_H #define SONNET_DIALOG_H #include "kateviewhelpers.h" class QListWidgetItem; class QModelIndex; namespace Sonnet { class BackgroundChecker; } /** * @short Spellcheck dialog * * \code * Sonnet::SpellCheckBar dlg = new Sonnet::SpellCheckBar( * new Sonnet::BackgroundChecker(this), this); * //connect signals * ... * dlg->setBuffer( someText ); * dlg->show(); * \endcode * * You can change buffer inside a slot connected to done() signal * and spellcheck will continue with new data automatically. */ class SpellCheckBar : public KateViewBarWidget { Q_OBJECT public: SpellCheckBar(Sonnet::BackgroundChecker *checker, QWidget *parent); - ~SpellCheckBar(); + ~SpellCheckBar() Q_DECL_OVERRIDE; QString originalBuffer() const; QString buffer() const; void closed() Q_DECL_OVERRIDE; void show(); void activeAutoCorrect(bool _active); /** * Controls whether an (indefinite) progress dialog is shown when the spell * checking takes longer than the given time to complete. By default no * progress dialog is shown. If the progress dialog is set to be shown, no * time consuming operation (for example, showing a notification message) should * be performed in a slot connected to the 'done' signal as this might trigger * the progress dialog unnecessarily. * * @param timeout time after which the progress dialog should appear; a negative * value can be used to hide it * @since 4.4 */ void showProgressDialog(int timeout = 500); /** * Controls whether a message box indicating the completion of the spell checking * is shown or not. By default it is not shown. * * @since 4.4 */ void showSpellCheckCompletionMessage(bool b = true); /** * Controls whether the spell checking is continued after the replacement of a * misspelled word has been performed. By default it is continued. * * @since 4.4 */ void setSpellCheckContinuedAfterReplacement(bool b); public Q_SLOTS: void setBuffer(const QString &); Q_SIGNALS: /** * The dialog won't be closed if you setBuffer() in slot connected to this signal * * Also emitted after stop() signal */ void done(const QString &newBuffer); void misspelling(const QString &word, int start); void replace(const QString &oldWord, int start, const QString &newWord); void stop(); void cancel(); void autoCorrect(const QString ¤tWord, const QString &replaceWord); /** * Signal sends when spell checking is finished/stopped/completed * @since 4.1 */ void spellCheckStatus(const QString &); /** * Emitted when the user changes the language used for spellchecking, * which is shown in a combobox of this dialog. * * @param language the new language the user selected * @since 4.1 */ void languageChanged(const QString &language); private Q_SLOTS: void slotMisspelling(const QString &word, int start); void slotDone(); void slotCancel(); void slotAddWord(); void slotReplaceWord(); void slotReplaceAll(); void slotSkip(); void slotSkipAll(); void slotSuggest(); void slotChangeLanguage(const QString &); void slotAutocorrect(); void setGuiEnabled(bool b); void setProgressDialogVisible(bool b); private: void updateDialog(const QString &word); void fillDictionaryComboBox(); void updateDictionaryComboBox(); void fillSuggestions(const QStringList &suggs); void initConnections(); void initGui(); void continueChecking(); private: class Private; Private *const d; Q_DISABLE_COPY(SpellCheckBar) }; #endif diff --git a/src/syntax/katehighlightingcmds.h b/src/syntax/katehighlightingcmds.h index a0031e98..d2380073 100644 --- a/src/syntax/katehighlightingcmds.h +++ b/src/syntax/katehighlightingcmds.h @@ -1,68 +1,68 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2014 Christoph Rüßler * * 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_HIGHLIGHT_RELOAD_H #define KATE_HIGHLIGHT_RELOAD_H #include namespace KateCommands { class Highlighting : public KTextEditor::Command { Highlighting() : KTextEditor::Command({ QStringLiteral("reload-highlighting"), QStringLiteral("edit-highlighting") }) { } static Highlighting* m_instance; public: - ~Highlighting() + ~Highlighting() Q_DECL_OVERRIDE { m_instance = nullptr; } static Highlighting *self() { if (m_instance == nullptr) { m_instance = new Highlighting(); } return m_instance; } /** * execute command * @param view view to use for execution * @param cmd cmd string * @param errorMsg error to return if no success * @return success */ bool exec(class KTextEditor::View *view, const QString &cmd, QString &errorMsg, const KTextEditor::Range &range = KTextEditor::Range::invalid()) Q_DECL_OVERRIDE; /** This command does not have help. @see KTextEditor::Command::help */ bool help(class KTextEditor::View *, const QString &, QString &) Q_DECL_OVERRIDE; }; } #endif diff --git a/src/utils/codecompletioninterface.cpp b/src/utils/codecompletioninterface.cpp index 9836c036..a5e5a102 100644 --- a/src/utils/codecompletioninterface.cpp +++ b/src/utils/codecompletioninterface.cpp @@ -1,26 +1,26 @@ /* This file is part of the KDE libraries Copyright (C) 2005-2006 Hamish Rodda * * 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. */ #include "codecompletioninterface.h" using namespace KTextEditor; -CodeCompletionInterface::~ CodeCompletionInterface() +CodeCompletionInterface::~CodeCompletionInterface() { } diff --git a/src/utils/katecmds.h b/src/utils/katecmds.h index b7000bc2..a6d1ecf3 100644 --- a/src/utils/katecmds.h +++ b/src/utils/katecmds.h @@ -1,212 +1,212 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2003-2005 Anders Lund * Copyright (C) 2001-2010 Christoph Cullmann * Copyright (C) 2001 Charles Samuels * * 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_CMDS_H__ #define __KATE_CMDS_H__ #include #include class KCompletion; /** * The KateCommands namespace collects subclasses of KTextEditor::Command * for specific use in kate. */ namespace KateCommands { /** * This KTextEditor::Command provides access to a lot of the core functionality * of kate part, settings, utilities, navigation etc. * it needs to get a kateview pointer, it will cast the kate::view pointer * hard to kateview */ class CoreCommands : public KTextEditor::Command { CoreCommands() : KTextEditor::Command({ QStringLiteral("indent") , QStringLiteral("unindent") , QStringLiteral("cleanindent") , QStringLiteral("fold") , QStringLiteral("tfold") , QStringLiteral("unfold") , QStringLiteral("comment") , QStringLiteral("uncomment") , QStringLiteral("goto") , QStringLiteral("kill-line") , QStringLiteral("set-tab-width") , QStringLiteral("set-replace-tabs") , QStringLiteral("set-show-tabs") , QStringLiteral("set-indent-width") , QStringLiteral("set-indent-mode") , QStringLiteral("set-auto-indent") , QStringLiteral("set-line-numbers") , QStringLiteral("set-folding-markers") , QStringLiteral("set-icon-border") , QStringLiteral("set-indent-pasted-text") , QStringLiteral("set-word-wrap") , QStringLiteral("set-word-wrap-column") , QStringLiteral("set-replace-tabs-save") , QStringLiteral("set-remove-trailing-spaces") , QStringLiteral("set-highlight") , QStringLiteral("set-mode") , QStringLiteral("set-show-indent") , QStringLiteral("print") }) { } static CoreCommands *m_instance; public: - ~CoreCommands() + ~CoreCommands() Q_DECL_OVERRIDE { m_instance = nullptr; } /** * execute command * @param view view to use for execution * @param cmd cmd string * @param errorMsg error to return if no success * @return success */ bool exec(class KTextEditor::View *view, const QString &cmd, QString &errorMsg); /** * execute command on given range * @param view view to use for execution * @param cmd cmd string * @param errorMsg error to return if no success * @param range range to execute command on * @return success */ bool exec(class KTextEditor::View *view, const QString &cmd, QString &errorMsg, const KTextEditor::Range &range = KTextEditor::Range(-1, -0, -1, 0)) Q_DECL_OVERRIDE; bool supportsRange(const QString &range) Q_DECL_OVERRIDE; /** This command does not have help. @see KTextEditor::Command::help */ bool help(class KTextEditor::View *, const QString &, QString &) Q_DECL_OVERRIDE; /** override from KTextEditor::Command */ KCompletion *completionObject(KTextEditor::View *, const QString &) Q_DECL_OVERRIDE; static CoreCommands *self() { if (m_instance == nullptr) { m_instance = new CoreCommands(); } return m_instance; } }; /** * insert a unicode or ascii character * base 9+1: 1234 * hex: 0x1234 or x1234 * octal: 01231 * * prefixed with "char:" **/ class Character : public KTextEditor::Command { Character() : KTextEditor::Command({ QStringLiteral("char") }) { } static Character *m_instance; public: - ~Character() + ~Character() Q_DECL_OVERRIDE { m_instance = nullptr; } /** * execute command * @param view view to use for execution * @param cmd cmd string * @param errorMsg error to return if no success * @return success */ bool exec(class KTextEditor::View *view, const QString &cmd, QString &errorMsg, const KTextEditor::Range &range = KTextEditor::Range(-1, -0, -1, 0)) Q_DECL_OVERRIDE; /** This command does not have help. @see KTextEditor::Command::help */ bool help(class KTextEditor::View *, const QString &, QString &) Q_DECL_OVERRIDE; static Character *self() { if (m_instance == nullptr) { m_instance = new Character(); } return m_instance; } }; /** * insert the current date/time in the given format */ class Date : public KTextEditor::Command { Date() : KTextEditor::Command({ QStringLiteral("date") }) { } static Date *m_instance; public: - ~Date() + ~Date() Q_DECL_OVERRIDE { m_instance = nullptr; } /** * execute command * @param view view to use for execution * @param cmd cmd string * @param errorMsg error to return if no success * @return success */ bool exec(class KTextEditor::View *view, const QString &cmd, QString &errorMsg, const KTextEditor::Range &range = KTextEditor::Range(-1, -0, -1, 0)) Q_DECL_OVERRIDE; /** This command does not have help. @see KTextEditor::Command::help */ bool help(class KTextEditor::View *, const QString &, QString &) Q_DECL_OVERRIDE; static Date *self() { if (m_instance == nullptr) { m_instance = new Date(); } return m_instance; } }; } // namespace KateCommands #endif diff --git a/src/utils/kateconfig.h b/src/utils/kateconfig.h index 5a47a85c..7a6c43fa 100644 --- a/src/utils/kateconfig.h +++ b/src/utils/kateconfig.h @@ -1,864 +1,864 @@ /* 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 = 0; /** * is a config session running */ bool configIsRunning = false; }; class KTEXTEDITOR_EXPORT KateGlobalConfig : public KateConfig { private: friend class KTextEditor::EditorPrivate; /** * only used in KTextEditor::EditorPrivate for the static global fallback !!! */ KateGlobalConfig(); /** * Destructor */ - ~KateGlobalConfig(); + ~KateGlobalConfig() Q_DECL_OVERRIDE; 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(); + ~KateDocumentConfig() Q_DECL_OVERRIDE; 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 = 2; int m_tabWidth = 4; uint m_tabHandling = tabSmart; uint m_configFlags = 0; int m_wordWrapAt = 80; 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 = 1; 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 = nullptr; }; 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(); + ~KateViewConfig() Q_DECL_OVERRIDE; 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); bool backspaceRemoveComposed() const; void setBackspaceRemoveComposed(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 = false; bool m_autoBrackets; bool m_backspaceRemoveComposed; 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; bool m_backspaceRemoveComposedSet : 1; private: static KateViewConfig *s_global; KTextEditor::ViewPrivate *m_view = nullptr; }; 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(); + ~KateRendererConfig() Q_DECL_OVERRIDE; 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); /** * Set the font but drop style name before that. * Otherwise e.g. styles like bold/italic/... will not work */ void setFontWithDroppedStyleName(const QFont &font); 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 = false; bool m_showIndentationLines = false; bool m_showWholeBracketExpression = false; bool m_animateBracketMatching = false; 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 = nullptr; }; #endif diff --git a/src/utils/katesedcmd.h b/src/utils/katesedcmd.h index a2075178..2dc9e5a8 100644 --- a/src/utils/katesedcmd.h +++ b/src/utils/katesedcmd.h @@ -1,145 +1,145 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2003-2005 Anders Lund * Copyright (C) 2001-2010 Christoph Cullmann * Copyright (C) 2001 Charles Samuels * * 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_SED_CMD_H__ #define __KATE_SED_CMD_H__ #include #include "kateregexpsearch.h" #include #include namespace KTextEditor { class DocumentPrivate; class ViewPrivate; } /** * The KateCommands namespace collects subclasses of KTextEditor::Command * for specific use in kate. */ namespace KateCommands { /** * Support vim/sed style search and replace * @author Charles Samuels **/ class SedReplace : public KTextEditor::Command { static SedReplace *m_instance; protected: SedReplace() : KTextEditor::Command({ QStringLiteral("s"), QStringLiteral("%s"), QStringLiteral("$s") }) { } public: - ~SedReplace() + ~SedReplace() Q_DECL_OVERRIDE { m_instance = nullptr; } /** * Execute command. Valid command strings are: * - s/search/replace/ find @c search, replace it with @c replace * on this line * - \%s/search/replace/ do the same to the whole file * - s/search/replace/i do the search and replace case insensitively * - $s/search/replace/ do the search are replacement to the * selection only * * @note $s/// is currently unsupported * @param view view to use for execution * @param cmd cmd string * @param errorMsg error to return if no success * @return success */ bool exec(class KTextEditor::View *view, const QString &cmd, QString &errorMsg, const KTextEditor::Range &r) Q_DECL_OVERRIDE; bool supportsRange(const QString &) Q_DECL_OVERRIDE { return true; } /** This command does not have help. @see KTextEditor::Command::help */ bool help(class KTextEditor::View *, const QString &, QString &) Q_DECL_OVERRIDE { return false; } static SedReplace *self() { if (m_instance == nullptr) { m_instance = new SedReplace(); } return m_instance; } /** * Parses @c sedReplaceString to see if it is a valid sed replace expression (e.g. "s/find/replace/gi"). * If it is, returns true and sets @c delimiter to the delimiter used in the expression; * @c destFindBeginPos and @c destFindEndPos to the positions in * @c sedReplaceString of the * begin and end of the "find" term; and @c destReplaceBeginPos and @c destReplaceEndPos to the begin * and end positions of the "replace" term. */ static bool parse(const QString &sedReplaceString, QString &destDelim, int &destFindBeginPos, int &destFindEndPos, int &destReplaceBeginPos, int &destReplaceEndPos); class InteractiveSedReplacer { public: InteractiveSedReplacer(KTextEditor::DocumentPrivate *doc, const QString &findPattern, const QString &replacePattern, bool caseSensitive, bool onlyOnePerLine, int startLine, int endLine); /** * Will return invalid Range if there are no further matches. */ KTextEditor::Range currentMatch(); void skipCurrentMatch(); void replaceCurrentMatch(); void replaceAllRemaining(); QString currentMatchReplacementConfirmationMessage(); QString finalStatusReportMessage(); private: const QString m_findPattern; const QString m_replacePattern; bool m_onlyOnePerLine; int m_endLine; KTextEditor::DocumentPrivate *m_doc; KateRegExpSearch m_regExpSearch; int m_numReplacementsDone; int m_numLinesTouched; int m_lastChangedLineNum; KTextEditor::Cursor m_currentSearchPos; const QVector fullCurrentMatch(); QString replacementTextForCurrentMatch(); }; protected: virtual bool interactiveSedReplace(KTextEditor::ViewPrivate *kateView, QSharedPointer interactiveSedReplace); }; } // namespace KateCommands #endif diff --git a/src/utils/katetemplatehandler.h b/src/utils/katetemplatehandler.h index 887c506b..474fb4dd 100644 --- a/src/utils/katetemplatehandler.h +++ b/src/utils/katetemplatehandler.h @@ -1,252 +1,252 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2004,2010 Joseph Wenninger * Copyright (C) 2009 Milian Wolff * Copyright (C) 2014 Sven Brauch * * 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_TEMPLATE_HANDLER_H_ #define _KATE_TEMPLATE_HANDLER_H_ #include #include #include #include #include #include class KateUndoManager; namespace KTextEditor { class DocumentPrivate; class ViewPrivate; class MovingCursor; class MovingRange; } /** * \brief Inserts a template and offers advanced snippet features, like navigation and mirroring. * * For each template inserted a new KateTemplateHandler will be created. * * The handler has the following features: * * \li It inserts the template string into the document at the requested position. * \li When the template contains at least one variable, the cursor will be placed * at the start of the first variable and its range gets selected. * \li When more than one variable exists,TAB and SHIFT TAB can be used to navigate * to the next/previous variable. * \li When a variable occurs more than once in the template, edits to any of the * occurrences will be mirroed to the other ones. * \li When ESC is pressed, the template handler closes. * \li When ALT + RETURN is pressed and a \c ${cursor} variable * exists in the template,the cursor will be placed there. Else the cursor will * be placed at the end of the template. * * \author Milian Wolff */ class KateTemplateHandler: public QObject { Q_OBJECT public: /** * Setup the template handler, insert the template string. * * NOTE: The handler deletes itself when required, you do not need to * keep track of it. */ KateTemplateHandler(KTextEditor::ViewPrivate *view, KTextEditor::Cursor position, const QString &templateString, const QString& script, KateUndoManager *undoManager); - virtual ~KateTemplateHandler(); + ~KateTemplateHandler() Q_DECL_OVERRIDE; protected: /** * \brief Provide keyboard interaction for the template handler. * * The event filter handles the following shortcuts: * * TAB: jump to next editable (i.e. not mirrored) range. * NOTE: this prevents indenting via TAB. * SHIFT + TAB: jump to previous editable (i.e. not mirrored) range. * NOTE: this prevents un-indenting via SHIFT + TAB. * ESC: terminate template handler (only when no completion is active). * ALT + RETURN: accept template and jump to the end-cursor. * if %{cursor} was given in the template, that will be the * end-cursor. * else just jump to the end of the inserted text. */ bool eventFilter(QObject *object, QEvent *event) Q_DECL_OVERRIDE; private: /** * Inserts the @p text template at @p position and performs * all necessary initializations, such as populating default values * and placing the cursor. */ void initializeTemplate(); /** * Parse @p templateText and populate m_fields. */ void parseFields(const QString& templateText); /** * Set necessary attributes (esp. background colour) on all moving * ranges for the fields in m_fields. */ void setupFieldRanges(); /** * Evaluate default values for all fields in m_fields and * store them in the fields. This updates the @property defaultValue property * of the TemplateField instances in m_fields from the raw, user-entered * default value to its evaluated equivalent (e.g. "func()" -> result of function call) * * @sa TemplateField */ void setupDefaultValues(); /** * Install an event filter on the filter proxy of \p view for * navigation between the ranges and terminating the KateTemplateHandler. * * \see eventFilter() */ void setupEventHandler(KTextEditor::View *view); /** * Jumps to the previous editable range. If there is none, wrap and jump to the first range. * * \see jumpToNextRange() */ void jumpToPreviousRange(); /** * Jumps to the next editable range. If there is none, wrap and jump to the last range. * * \see jumpToPreviousRange() */ void jumpToNextRange(); /** * Helper function for jumpTo{Next,Previous} * if initial is set to true, assumes the cursor is before the snippet * and selects the first field */ void jump(int by, bool initial = false); /** * Jumps to the final cursor position. This is either \p m_finalCursorPosition, or * if that is not set, the end of \p m_templateRange. */ void jumpToFinalCursorPosition(); /** * Go through all template fields and decide if their moving ranges expand * when edited at the corners. Expansion is turned off if two fields are * directly adjacent to avoid overlaps when characters are inserted between * them. */ void updateRangeBehaviours(); /** * Sort all template fields in m_fields by tab order, which means, * by range; except for ${cursor} which is always sorted last. */ void sortFields(); private Q_SLOTS: /** * Saves the range of the inserted template. This is required since * tabs could get expanded on insert. While we are at it, we can * use it to auto-indent the code after insert. */ void slotTemplateInserted(KTextEditor::Document *document, const KTextEditor::Range &range); /** * Install event filter on new views. */ void slotViewCreated(KTextEditor::Document *document, KTextEditor::View *view); /** * Update content of all dependent fields, i.e. mirror or script fields. */ void updateDependentFields(KTextEditor::Document *document, const KTextEditor::Range &oldRange); public: KTextEditor::ViewPrivate* view() const; KTextEditor::DocumentPrivate* doc() const; private: /// The view we operate on KTextEditor::ViewPrivate* m_view; /// The undo manager associated with our document KateUndoManager* const m_undoManager; // Describes a single template field, e.g. ${foo}. struct TemplateField { // up-to-date range for the field QSharedPointer range; // contents of the field, i.e. identifier or function to call QString identifier; // default value, if applicable; else empty QString defaultValue; enum Kind { Invalid, // not an actual field Editable, // normal, user-editable field (green by default) [non-dependent field] Mirror, // field mirroring contents of another field [dependent field] FunctionCall, // field containing the up-to-date result of a function call [dependent field] FinalCursorPosition // field marking the final cursor position }; Kind kind = Invalid; // true if this field was edited by the user before bool touched = false; bool operator==(const TemplateField& other) { return range == other.range; } }; // List of all template fields in the inserted snippet. @see sortFields() QVector m_fields; // Get the template field which contains @p range. const TemplateField fieldForRange(const KTextEditor::Range& range) const; /// Construct a map of master fields and their current value, for use in scripts. KateScript::FieldMap fieldMap() const; /// A range that occupies the whole range of the inserted template. /// When the an edit happens outside it, the template handler gets closed. QSharedPointer m_wholeTemplateRange; /// Set to true when currently updating dependent fields, to prevent recursion. bool m_internalEdit; /// template script (i.e. javascript stuff), which can be used by the current template KateScript m_templateScript; }; #endif diff --git a/src/variableeditor/variableeditor.h b/src/variableeditor/variableeditor.h index 46ba1346..14f5e93d 100644 --- a/src/variableeditor/variableeditor.h +++ b/src/variableeditor/variableeditor.h @@ -1,187 +1,187 @@ /* This file is part of the KDE project Copyright (C) 2011 Dominik Haumann 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 VARIABLE_EDITOR_H #define VARIABLE_EDITOR_H #include class KateHelpButton; class VariableBoolItem; class VariableColorItem; class VariableFontItem; class VariableItem; class VariableStringListItem; class VariableIntItem; class VariableStringItem; class VariableSpellCheckItem; class VariableRemoveSpacesItem; class KColorCombo; class QFontComboBox; class QCheckBox; class QComboBox; class QLabel; class QLineEdit; class QSpinBox; namespace Sonnet { class DictionaryComboBox; } class VariableEditor : public QWidget { Q_OBJECT public: VariableEditor(VariableItem *item, QWidget *parent = nullptr); - virtual ~VariableEditor(); + ~VariableEditor() Q_DECL_OVERRIDE; VariableItem *item() const; Q_SIGNALS: void valueChanged(); protected Q_SLOTS: void itemEnabled(bool enabled); void activateItem(); protected: void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; void enterEvent(QEvent *event) Q_DECL_OVERRIDE; void leaveEvent(QEvent *event) Q_DECL_OVERRIDE; private: VariableItem *m_item; QCheckBox *m_checkBox; QLabel *m_variable; QLabel *m_helpText; KateHelpButton *m_btnHelp; }; class VariableIntEditor : public VariableEditor { Q_OBJECT public: VariableIntEditor(VariableIntItem *item, QWidget *parent); protected Q_SLOTS: void setItemValue(int newValue); private: QSpinBox *m_spinBox; }; class VariableBoolEditor : public VariableEditor { Q_OBJECT public: VariableBoolEditor(VariableBoolItem *item, QWidget *parent); protected Q_SLOTS: void setItemValue(int enabled); private: QComboBox *m_comboBox; }; class VariableStringListEditor : public VariableEditor { Q_OBJECT public: VariableStringListEditor(VariableStringListItem *item, QWidget *parent); protected Q_SLOTS: void setItemValue(const QString &newValue); private: QComboBox *m_comboBox; }; class VariableColorEditor : public VariableEditor { Q_OBJECT public: VariableColorEditor(VariableColorItem *item, QWidget *parent); protected Q_SLOTS: void setItemValue(const QColor &newValue); private: KColorCombo *m_comboBox; }; class VariableFontEditor : public VariableEditor { Q_OBJECT public: VariableFontEditor(VariableFontItem *item, QWidget *parent); protected Q_SLOTS: void setItemValue(const QFont &newValue); private: QFontComboBox *m_comboBox; }; class VariableStringEditor : public VariableEditor { Q_OBJECT public: VariableStringEditor(VariableStringItem *item, QWidget *parent); protected Q_SLOTS: void setItemValue(const QString &newValue); private: QLineEdit *m_lineEdit; }; class VariableSpellCheckEditor : public VariableEditor { Q_OBJECT public: VariableSpellCheckEditor(VariableSpellCheckItem *item, QWidget *parent); protected Q_SLOTS: void setItemValue(const QString &newValue); private: Sonnet::DictionaryComboBox *m_dictionaryCombo; }; class VariableRemoveSpacesEditor : public VariableEditor { Q_OBJECT public: VariableRemoveSpacesEditor(VariableRemoveSpacesItem *item, QWidget *parent); protected Q_SLOTS: void setItemValue(int enabled); private: QComboBox *m_comboBox; }; #endif diff --git a/src/variableeditor/variablelistview.h b/src/variableeditor/variablelistview.h index a4cd9064..70d408bf 100644 --- a/src/variableeditor/variablelistview.h +++ b/src/variableeditor/variablelistview.h @@ -1,60 +1,60 @@ /* This file is part of the KDE project Copyright (C) 2011 Dominik Haumann 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 VARIABLE_LIST_VIEW_H #define VARIABLE_LIST_VIEW_H #include #include class VariableItem; class VariableEditor; class VariableListView : public QScrollArea { Q_OBJECT public: VariableListView(const QString &variableLine, QWidget *parent = nullptr); - virtual ~VariableListView(); + ~VariableListView() Q_DECL_OVERRIDE; void addItem(VariableItem *item); /// always returns the up-to-date variables line QString variableLine(); Q_SIGNALS: void aboutToHide(); void changed(); // unused right now protected: void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE; void hideEvent(QHideEvent *event) Q_DECL_OVERRIDE; void parseVariables(const QString &line); QVector m_items; QVector m_editors; QMap m_variables; }; #endif diff --git a/src/view/katestatusbar.h b/src/view/katestatusbar.h index 1c399bb1..486e1c1e 100644 --- a/src/view/katestatusbar.h +++ b/src/view/katestatusbar.h @@ -1,109 +1,109 @@ /* This file is part of the KDE and the Kate project * * Copyright (C) 2013 Dominik Haumann * * 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_STATUS_BAR_H #define KATE_STATUS_BAR_H #include "kateview.h" #include "kateviewhelpers.h" #include #include #include #include #include class WordCounter; class KateStatusBarOpenUpMenu: public QMenu { Q_OBJECT public: KateStatusBarOpenUpMenu(QWidget *parent); - virtual ~KateStatusBarOpenUpMenu(); + ~KateStatusBarOpenUpMenu() Q_DECL_OVERRIDE; void setVisible(bool) Q_DECL_OVERRIDE; }; class KateStatusBar : public KateViewBarWidget { Q_OBJECT public: explicit KateStatusBar(KTextEditor::ViewPrivate *view); public Q_SLOTS: void updateStatus (); void viewModeChanged (); void cursorPositionChanged (); void selectionChanged (); void modifiedChanged(); void documentConfigChanged (); void modeChanged (); void wordCountChanged(int, int, int, int); void toggleWordCount(bool on); void configChanged(); protected: bool eventFilter(QObject *obj, QEvent *event) Q_DECL_OVERRIDE; private: KTextEditor::ViewPrivate *const m_view; QLabel* m_lineColLabel; QLabel* m_wordCountLabel; QToolButton* m_modifiedLabel; QLabel* m_insertModeLabel; QPushButton* m_mode; QPushButton* m_encoding; QPushButton* m_tabsIndent; KLocalizedString m_spacesOnly; KLocalizedString m_tabsOnly; KLocalizedString m_tabSpacesMixed; KLocalizedString m_spacesOnlyShowTabs; QMenu *m_indentSettingsMenu; unsigned int m_modifiedStatus; unsigned int m_selectionMode; QActionGroup *m_tabGroup; QActionGroup *m_indentGroup; QAction *m_mixedAction; QAction *m_hardAction; QAction *m_softAction; WordCounter *m_wordCounter; private: void addNumberAction(QActionGroup *group, QMenu *menu, int data); void updateGroup(QActionGroup *group, int w); public Q_SLOTS: void slotTabGroup(QAction*); void slotIndentGroup(QAction*); void slotIndentTabMode(QAction*); }; #endif diff --git a/src/view/katetextpreview.h b/src/view/katetextpreview.h index fb456a9c..00568240 100644 --- a/src/view/katetextpreview.h +++ b/src/view/katetextpreview.h @@ -1,106 +1,106 @@ /* This file is part of the KDE libraries Copyright (C) 2016 Dominik Haumann This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #ifndef KATE_TEXT_PREVIEW_H #define KATE_TEXT_PREVIEW_H #include //namespace KTextEditor { class DocumentPrivate; } namespace KTextEditor { class ViewPrivate; } /** * TODO */ class KateTextPreview : public QFrame { Q_OBJECT Q_PROPERTY(qreal line READ line WRITE setLine) Q_PROPERTY(bool showFoldedLines READ showFoldedLines WRITE setShowFoldedLines) Q_PROPERTY(bool centerView READ centerView WRITE setCenterView) Q_PROPERTY(qreal scaleFactor READ scaleFactor WRITE setScaleFactor) public: KateTextPreview(KTextEditor::ViewPrivate *view); - virtual ~KateTextPreview(); + ~KateTextPreview() Q_DECL_OVERRIDE; KTextEditor::ViewPrivate *view() const; /** * Sets @p line as preview line. */ void setLine(qreal line); /** * Returns the line set with setLine(). */ qreal line() const; /** * Enabled/disable centering the view on the line set with setLine(). * If @p center is false, the first visible line is the once specified in * setLine(). If @p center is true, the specified line is vertically * centered. By default, centering the preview is set to true. */ void setCenterView(bool center); /** * Returns whether view centering is enabled. */ bool centerView() const; /** * Sets the scale factor. * The default scale factor is 1.0. For text previews, you may want a scale * factor of e.g. 0.8 or 0.9. Negative scale factors are not allowed. */ void setScaleFactor(qreal factor); /** * Returns the scale factor set with setScale(). * The default value is 1.0. */ qreal scaleFactor() const; /** * Sets whether folded lines are hidden or not. * By default, folded liens are not visible. */ void setShowFoldedLines(bool on); /** * Returns whether folded lines are hidden. */ bool showFoldedLines() const; protected: void paintEvent(QPaintEvent *event) Q_DECL_OVERRIDE; private: KTextEditor::ViewPrivate *m_view; qreal m_line; bool m_showFoldedLines; bool m_center; qreal m_scale; }; #endif diff --git a/src/view/kateview.h b/src/view/kateview.h index c8500a18..078b5fa1 100644 --- a/src/view/kateview.h +++ b/src/view/kateview.h @@ -1,997 +1,997 @@ /* This file is part of the KDE libraries Copyright (C) 2002 John Firebaugh Copyright (C) 2001 Christoph Cullmann Copyright (C) 2001-2010 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_view_h #define kate_view_h #include #include #include #include #include #include #include #include #include #include #include #include #include #include "katetextrange.h" #include "katetextfolding.h" #include "katerenderer.h" namespace KTextEditor { class AnnotationModel; class Message; } namespace KTextEditor { class DocumentPrivate; } class KateBookmarks; class KateViewConfig; class KateRenderer; class KateSpellCheckDialog; class KateCompletionWidget; class KateViewInternal; class KateViewBar; class KateTextPreview; class KateGotoBar; class KateDictionaryBar; class KateSpellingMenu; class KateMessageWidget; class KateIconBorder; class KateStatusBar; class KateViewEncodingAction; class KateModeMenu; class KateAbstractInputMode; class KateScriptActionMenu; class KateMessageLayout; class KToggleAction; class KSelectAction; class QAction; namespace KTextEditor { // // Kate KTextEditor::View class ;) // class KTEXTEDITOR_EXPORT ViewPrivate : public KTextEditor::View, public KTextEditor::TextHintInterface, public KTextEditor::CodeCompletionInterface, public KTextEditor::ConfigInterface, public KTextEditor::AnnotationViewInterface { Q_OBJECT Q_INTERFACES(KTextEditor::TextHintInterface) Q_INTERFACES(KTextEditor::ConfigInterface) Q_INTERFACES(KTextEditor::CodeCompletionInterface) Q_INTERFACES(KTextEditor::AnnotationViewInterface) friend class KTextEditor::View; friend class ::KateViewInternal; friend class ::KateIconBorder; friend class ::KateTextPreview; public: ViewPrivate (KTextEditor::DocumentPrivate *doc, QWidget *parent, KTextEditor::MainWindow *mainWindow = nullptr); - ~ViewPrivate (); + ~ViewPrivate() Q_DECL_OVERRIDE; /** * Get the view's main window, if any * \return the view's main window */ KTextEditor::MainWindow *mainWindow() const Q_DECL_OVERRIDE { return m_mainWindow; } KTextEditor::Document *document() const Q_DECL_OVERRIDE; ViewMode viewMode() const Q_DECL_OVERRIDE; QString viewModeHuman() const Q_DECL_OVERRIDE; InputMode viewInputMode() const Q_DECL_OVERRIDE; QString viewInputModeHuman() const Q_DECL_OVERRIDE; void setInputMode(InputMode mode); // // KTextEditor::ClipboardInterface // public Q_SLOTS: void paste(const QString *textToPaste = nullptr); void cut(); void copy() const; private Q_SLOTS: /** * internal use, apply word wrap */ void applyWordWrap(); // // KTextEditor::PopupMenuInterface // public: void setContextMenu(QMenu *menu) Q_DECL_OVERRIDE; QMenu *contextMenu() const Q_DECL_OVERRIDE; QMenu *defaultContextMenu(QMenu *menu = nullptr) const Q_DECL_OVERRIDE; private Q_SLOTS: void aboutToShowContextMenu(); void aboutToHideContextMenu(); private: QPointer m_contextMenu; // // KTextEditor::ViewCursorInterface // public: bool setCursorPosition(KTextEditor::Cursor position) Q_DECL_OVERRIDE; KTextEditor::Cursor cursorPosition() const Q_DECL_OVERRIDE; KTextEditor::Cursor cursorPositionVirtual() const Q_DECL_OVERRIDE; QPoint cursorToCoordinate(const KTextEditor::Cursor &cursor) const Q_DECL_OVERRIDE; KTextEditor::Cursor coordinatesToCursor(const QPoint &coord) const Q_DECL_OVERRIDE; QPoint cursorPositionCoordinates() const Q_DECL_OVERRIDE; bool setCursorPositionVisual(const KTextEditor::Cursor &position); /** * Return the virtual cursor column, each tab is expanded into the * document's tabWidth characters. If word wrap is off, the cursor may be * behind the line's length. */ int virtualCursorColumn() const; bool mouseTrackingEnabled() const Q_DECL_OVERRIDE; bool setMouseTrackingEnabled(bool enable) Q_DECL_OVERRIDE; private: void notifyMousePositionChanged(const KTextEditor::Cursor &newPosition); // Internal public: bool setCursorPositionInternal(const KTextEditor::Cursor &position, uint tabwidth = 1, bool calledExternally = false); // // KTextEditor::ConfigInterface // public: QStringList configKeys() const Q_DECL_OVERRIDE; QVariant configValue(const QString &key) Q_DECL_OVERRIDE; void setConfigValue(const QString &key, const QVariant &value) Q_DECL_OVERRIDE; Q_SIGNALS: void configChanged(); public: /** * Try to fold starting at the given line. * This will both try to fold existing folding ranges of this line and to query the highlighting what to fold. * @param startLine start line to fold at */ void foldLine(int startLine); /** * Try to unfold all foldings starting at the given line. * @param startLine start line to unfold at */ void unfoldLine(int startLine); // // KTextEditor::CodeCompletionInterface2 // public: bool isCompletionActive() const Q_DECL_OVERRIDE; void startCompletion(const KTextEditor::Range &word, KTextEditor::CodeCompletionModel *model) Q_DECL_OVERRIDE; void abortCompletion() Q_DECL_OVERRIDE; void forceCompletion() Q_DECL_OVERRIDE; void registerCompletionModel(KTextEditor::CodeCompletionModel *model) Q_DECL_OVERRIDE; void unregisterCompletionModel(KTextEditor::CodeCompletionModel *model) Q_DECL_OVERRIDE; bool isCompletionModelRegistered(KTextEditor::CodeCompletionModel *model) const; bool isAutomaticInvocationEnabled() const Q_DECL_OVERRIDE; void setAutomaticInvocationEnabled(bool enabled = true) Q_DECL_OVERRIDE; Q_SIGNALS: void completionExecuted(KTextEditor::View *view, const KTextEditor::Cursor &position, KTextEditor::CodeCompletionModel *model, const QModelIndex &); void completionAborted(KTextEditor::View *view); public Q_SLOTS: void userInvokedCompletion(); public: KateCompletionWidget *completionWidget() const; mutable KateCompletionWidget *m_completionWidget; void sendCompletionExecuted(const KTextEditor::Cursor &position, KTextEditor::CodeCompletionModel *model, const QModelIndex &index); void sendCompletionAborted(); // // KTextEditor::TextHintInterface // public: void registerTextHintProvider(KTextEditor::TextHintProvider *provider) Q_DECL_OVERRIDE; void unregisterTextHintProvider(KTextEditor::TextHintProvider *provider) Q_DECL_OVERRIDE; void setTextHintDelay(int delay) Q_DECL_OVERRIDE; int textHintDelay() const Q_DECL_OVERRIDE; public: bool dynWordWrap() const { return m_hasWrap; } // // KTextEditor::SelectionInterface stuff // public Q_SLOTS: bool setSelection(const KTextEditor::Range &selection) Q_DECL_OVERRIDE; bool removeSelection() Q_DECL_OVERRIDE { return clearSelection(); } bool removeSelectionText() Q_DECL_OVERRIDE { return removeSelectedText(); } bool setBlockSelection(bool on) Q_DECL_OVERRIDE; bool toggleBlockSelection(); bool clearSelection(); bool clearSelection(bool redraw, bool finishedChangingSelection = true); bool removeSelectedText(); bool selectAll(); public: bool selection() const Q_DECL_OVERRIDE; QString selectionText() const Q_DECL_OVERRIDE; bool blockSelection() const Q_DECL_OVERRIDE; KTextEditor::Range selectionRange() const Q_DECL_OVERRIDE; static void blockFix(KTextEditor::Range &range); // // Arbitrary Syntax HL + Action extensions // public: // Action association extension void deactivateEditActions(); void activateEditActions(); // // internal helper stuff, for katerenderer and so on // public: // should cursor be wrapped ? take config + blockselection state in account bool wrapCursor() const; // some internal functions to get selection state of a line/col bool cursorSelected(const KTextEditor::Cursor &cursor); bool lineSelected(int line); bool lineEndSelected(const KTextEditor::Cursor &lineEndPos); bool lineHasSelected(int line); bool lineIsSelection(int line); void ensureCursorColumnValid(); void tagSelection(const KTextEditor::Range &oldSelection); void selectWord(const KTextEditor::Cursor &cursor); void selectLine(const KTextEditor::Cursor &cursor); //BEGIN EDIT STUFF public: void editStart(); void editEnd(int editTagLineStart, int editTagLineEnd, bool tagFrom); void editSetCursor(const KTextEditor::Cursor &cursor); //END //BEGIN TAG & CLEAR public: bool tagLine(const KTextEditor::Cursor &virtualCursor); bool tagRange(const KTextEditor::Range &range, bool realLines = false); bool tagLines(int start, int end, bool realLines = false); bool tagLines(KTextEditor::Cursor start, KTextEditor::Cursor end, bool realCursors = false); bool tagLines(KTextEditor::Range range, bool realRange = false); void tagAll(); void clear(); void repaintText(bool paintOnlyDirty = false); void updateView(bool changed = false); //END // // KTextEditor::AnnotationView // public: void setAnnotationModel(KTextEditor::AnnotationModel *model) Q_DECL_OVERRIDE; KTextEditor::AnnotationModel *annotationModel() const Q_DECL_OVERRIDE; void setAnnotationBorderVisible(bool visible) Q_DECL_OVERRIDE; bool isAnnotationBorderVisible() const Q_DECL_OVERRIDE; Q_SIGNALS: void annotationContextMenuAboutToShow(KTextEditor::View *view, QMenu *menu, int line) Q_DECL_OVERRIDE; void annotationActivated(KTextEditor::View *view, int line) Q_DECL_OVERRIDE; // KF6: fix View -> KTextEditor::View void annotationBorderVisibilityChanged(View *view, bool visible) Q_DECL_OVERRIDE; void navigateLeft(); void navigateRight(); void navigateUp(); void navigateDown(); void navigateAccept(); void navigateBack(); private: KTextEditor::AnnotationModel *m_annotationModel; // // KTextEditor::View // public: void emitNavigateLeft() { emit navigateLeft(); } void emitNavigateRight() { emit navigateRight(); } void emitNavigateUp() { emit navigateUp(); } void emitNavigateDown() { emit navigateDown(); } void emitNavigateAccept() { emit navigateAccept(); } void emitNavigateBack() { emit navigateBack(); } /** Return values for "save" related commands. */ bool isOverwriteMode() const; QString currentTextLine(); QTextLayout * textLayout(int line) const; QTextLayout * textLayout(const KTextEditor::Cursor &pos) const; public Q_SLOTS: void indent(); void unIndent(); void cleanIndent(); void align(); void comment(); void uncomment(); void toggleComment(); void killLine(); /** * Sets the cursor to the previous editing position in this document */ void goToPreviousEditingPosition(); /** * Sets the cursor to the next editing position in this document */ void goToNextEditingPosition(); /** Uppercases selected text, or an alphabetic character next to the cursor. */ void uppercase(); /** Lowercases selected text, or an alphabetic character next to the cursor. */ void lowercase(); /** Capitalizes the selection (makes each word start with an uppercase) or the word under the cursor. */ void capitalize(); /** Joins lines touched by the selection */ void joinLines(); // Note - the following functions simply forward to KateViewInternal void keyReturn(); void smartNewline(); void backspace(); void deleteWordLeft(); void keyDelete(); void deleteWordRight(); void transpose(); void cursorLeft(); void shiftCursorLeft(); void cursorRight(); void shiftCursorRight(); void wordLeft(); void shiftWordLeft(); void wordRight(); void shiftWordRight(); void home(); void shiftHome(); void end(); void shiftEnd(); void up(); void shiftUp(); void down(); void shiftDown(); void scrollUp(); void scrollDown(); void topOfView(); void shiftTopOfView(); void bottomOfView(); void shiftBottomOfView(); void pageUp(); void shiftPageUp(); void pageDown(); void shiftPageDown(); void top(); void shiftTop(); void bottom(); void shiftBottom(); void toMatchingBracket(); void shiftToMatchingBracket(); void toPrevModifiedLine(); void toNextModifiedLine(); void insertTab(); void gotoLine(); // config file / session management functions public: /** * Read session settings from the given \p config. * * Known flags: * "SkipUrl" => don't save/restore the file * "SkipMode" => don't save/restore the mode * "SkipHighlighting" => don't save/restore the highlighting * "SkipEncoding" => don't save/restore the encoding * * \param config read the session settings from this KConfigGroup * \param flags additional flags * \see writeSessionConfig() */ void readSessionConfig(const KConfigGroup &config, const QSet &flags = QSet()) Q_DECL_OVERRIDE; /** * Write session settings to the \p config. * See readSessionConfig() for more details. * * \param config write the session settings to this KConfigGroup * \param flags additional flags * \see readSessionConfig() */ void writeSessionConfig(KConfigGroup &config, const QSet &flags = QSet()) Q_DECL_OVERRIDE; public Q_SLOTS: void setEol(int eol); void setAddBom(bool enabled); void find(); void findSelectedForwards(); void findSelectedBackwards(); void replace(); void findNext(); void findPrevious(); void setFoldingMarkersOn(bool enable); // Not in KTextEditor::View, but should be void setIconBorder(bool enable); void setLineNumbersOn(bool enable); void setScrollBarMarks(bool enable); void setScrollBarMiniMap(bool enable); void setScrollBarMiniMapAll(bool enable); void setScrollBarMiniMapWidth(int width); void toggleFoldingMarkers(); void toggleIconBorder(); void toggleLineNumbersOn(); void toggleScrollBarMarks(); void toggleScrollBarMiniMap(); void toggleScrollBarMiniMapAll(); void toggleDynWordWrap(); void setDynWrapIndicators(int mode); public: int getEol() const; public: KateRenderer *renderer(); bool iconBorder(); bool lineNumbersOn(); bool scrollBarMarks(); bool scrollBarMiniMap(); bool scrollBarMiniMapAll(); int dynWrapIndicators(); bool foldingMarkersOn(); private Q_SLOTS: /** * used to update actions after selection changed */ void slotSelectionChanged(); void toggleInputMode(); void cycleInputMode(); public: /** * accessor to katedocument pointer * @return pointer to document */ KTextEditor::DocumentPrivate *doc() { return m_doc; } const KTextEditor::DocumentPrivate *doc() const { return m_doc; } public Q_SLOTS: void slotUpdateUndo(); void toggleInsert(); void reloadFile(); void toggleWWMarker(); void toggleNPSpaces(); void toggleWordCount(bool on); void toggleWriteLock(); void switchToCmdLine(); void slotReadWriteChanged(); void slotClipboardHistoryChanged(); Q_SIGNALS: void dropEventPass(QDropEvent *); public: /** * Folding handler for this view. * @return folding handler */ Kate::TextFolding &textFolding() { return m_textFolding; } public: void slotTextInserted(KTextEditor::View *view, const KTextEditor::Cursor &position, const QString &text); void exportHtmlToFile(const QString &file); private Q_SLOTS: void slotGotFocus(); void slotLostFocus(); void slotSaveCanceled(const QString &error); void slotConfigDialog(); void exportHtmlToClipboard (); void exportHtmlToFile (); public Q_SLOTS: void slotFoldToplevelNodes(); void slotExpandToplevelNodes(); void slotCollapseLocal(); void slotExpandLocal(); private: void setupLayout(); void setupConnections(); void setupActions(); void setupEditActions(); void setupCodeFolding(); QList m_editActions; QAction *m_editUndo; QAction *m_editRedo; QAction *m_pasteMenu; KToggleAction *m_toggleFoldingMarkers; KToggleAction *m_toggleIconBar; KToggleAction *m_toggleLineNumbers; KToggleAction *m_toggleScrollBarMarks; KToggleAction *m_toggleScrollBarMiniMap; KToggleAction *m_toggleScrollBarMiniMapAll; KToggleAction *m_toggleDynWrap; KSelectAction *m_setDynWrapIndicators; KToggleAction *m_toggleWWMarker; KToggleAction *m_toggleNPSpaces; KToggleAction *m_toggleWordCount; QAction *m_switchCmdLine; KToggleAction *m_viInputModeAction; KSelectAction *m_setEndOfLine; KToggleAction *m_addBom; QAction *m_cut; QAction *m_copy; QAction *m_copyHtmlAction; QAction *m_paste; QAction *m_selectAll; QAction *m_deSelect; QActionGroup *m_inputModeActions; KToggleAction *m_toggleBlockSelection; KToggleAction *m_toggleInsert; KToggleAction *m_toggleWriteLock; bool m_hasWrap; KTextEditor::DocumentPrivate *const m_doc; Kate::TextFolding m_textFolding; KateViewConfig *const m_config; KateRenderer *const m_renderer; KateViewInternal *const m_viewInternal; KateSpellCheckDialog *m_spell; KateBookmarks *const m_bookmarks; //* margins QSpacerItem *m_topSpacer; QSpacerItem *m_leftSpacer; QSpacerItem *m_rightSpacer; QSpacerItem *m_bottomSpacer; private Q_SLOTS: void slotHlChanged(); /** * Configuration */ public: inline KateViewConfig *config() { return m_config; } void updateConfig(); void updateDocumentConfig(); void updateRendererConfig(); private Q_SLOTS: void updateFoldingConfig(); private: bool m_startingUp; bool m_updatingDocumentConfig; // stores the current selection Kate::TextRange m_selection; // do we select normal or blockwise ? bool blockSelect; // templates public: bool insertTemplateInternal(const KTextEditor::Cursor& insertPosition, const QString& templateString, const QString& script = QString()); /** * Accessors to the bars... */ public: KateViewBar *bottomViewBar() const; KateDictionaryBar *dictionaryBar(); private: KateGotoBar *gotoBar(); /** * viewbar + its widgets * they are created on demand... */ private: // created in constructor of the view KateViewBar *m_bottomViewBar; // created on demand..., only access them through the above accessors.... KateGotoBar *m_gotoBar; KateDictionaryBar *m_dictionaryBar; // input modes public: KateAbstractInputMode *currentInputMode() const; public: KTextEditor::Range visibleRange(); Q_SIGNALS: void displayRangeChanged(KTextEditor::ViewPrivate *view); protected: bool event(QEvent *e) Q_DECL_OVERRIDE; void paintEvent(QPaintEvent *e) Q_DECL_OVERRIDE; KToggleAction *m_toggleOnTheFlySpellCheck; KateSpellingMenu *m_spellingMenu; protected Q_SLOTS: void toggleOnTheFlySpellCheck(bool b); public Q_SLOTS: void changeDictionary(); void reflectOnTheFlySpellCheckStatus(bool enabled); public: KateSpellingMenu *spellingMenu(); private: bool m_userContextMenuSet; private Q_SLOTS: /** * save folding state before document reload */ void saveFoldingState(); /** * restore folding state after document reload */ void applyFoldingState(); void clearHighlights(); void createHighlights(); private: void selectionChangedForHighlights(); /** * saved folding state */ QJsonDocument m_savedFoldingState; QString m_currentTextForHighlights; QList m_rangesForHighlights; public: /** * Attribute of a range changed or range with attribute changed in given line range. * @param startLine start line of change * @param endLine end line of change * @param rangeWithAttribute attribute changed or is active, this will perhaps lead to repaints */ void notifyAboutRangeChange(int startLine, int endLine, bool rangeWithAttribute); private Q_SLOTS: /** * Delayed update for view after text ranges changed */ void slotDelayedUpdateOfView(); Q_SIGNALS: /** * Delayed update for view after text ranges changed */ void delayedUpdateOfView(); public: /** * set of ranges which had the mouse inside last time, used for rendering * @return set of ranges which had the mouse inside last time checked */ const QSet *rangesMouseIn() const { return &m_rangesMouseIn; } /** * set of ranges which had the caret inside last time, used for rendering * @return set of ranges which had the caret inside last time checked */ const QSet *rangesCaretIn() const { return &m_rangesCaretIn; } /** * check if ranges changed for mouse in and caret in * @param activationType type of activation to check */ void updateRangesIn(KTextEditor::Attribute::ActivationType activationType); // // helpers for delayed view update after ranges changes // private: /** * update already inited? */ bool m_delayedUpdateTriggered; /** * minimal line to update */ int m_lineToUpdateMin; /** * maximal line to update */ int m_lineToUpdateMax; /** * set of ranges which had the mouse inside last time */ QSet m_rangesMouseIn; /** * set of ranges which had the caret inside last time */ QSet m_rangesCaretIn; // // forward impl for KTextEditor::MessageInterface // public: /** * Used by Document::postMessage(). */ void postMessage(KTextEditor::Message *message, QList > actions); private: /** * Message widgets showing KTextEditor::Messages. * The index of the array maps to the enum KTextEditor::Message::MessagePosition. */ std::array m_messageWidgets{{nullptr}}; /** Layout for floating notifications */ KateMessageLayout *m_notificationLayout = nullptr; // for unit test 'tests/messagetest.cpp' public: KateMessageWidget *messageWidget(); private: /** * The main window responsible for this view, if any */ QPointer m_mainWindow; // // KTextEditor::PrintInterface // public Q_SLOTS: bool print() Q_DECL_OVERRIDE; void printPreview() Q_DECL_OVERRIDE; public: /** * Get the view status bar * @return status bar, in enabled */ KateStatusBar *statusBar () const { return m_statusBar; } /** * Toogle status bar, e.g. create or remove it */ void toggleStatusBar (); /** * Get the encoding menu * @return the encoding menu */ KateViewEncodingAction *encodingAction () const { return m_encodingAction; } /** * Get the mode menu * @return the mode menu */ KateModeMenu *modeAction () const { return m_modeAction; } private: /** * the status bar of this view */ KateStatusBar *m_statusBar; /** * the encoding selection menu, used by view + status bar */ KateViewEncodingAction *m_encodingAction; /** * the mode selection menu, used by view + status bar */ KateModeMenu *m_modeAction; /** * is automatic invocation of completion disabled temporarily? */ bool m_temporaryAutomaticInvocationDisabled; public: /** * Returns the attribute for the default style \p defaultStyle. */ Attribute::Ptr defaultStyleAttribute(DefaultStyle defaultStyle) const Q_DECL_OVERRIDE; /** * Get the list of AttributeBlocks for a given \p line in the document. * * \return list of AttributeBlocks for given \p line. */ QList lineAttributes(int line) Q_DECL_OVERRIDE; private: // remember folding state to prevent refolding the first line if it was manually unfolded, // e.g. when saving a file or changing other config vars bool m_autoFoldedFirstLine; public: void setScrollPositionInternal(KTextEditor::Cursor &cursor); void setHorizontalScrollPositionInternal(int x); KTextEditor::Cursor maxScrollPositionInternal() const; int firstDisplayedLineInternal(LineType lineType) const; int lastDisplayedLineInternal(LineType lineType) const; QRect textAreaRectInternal() const; private: /** * script action menu, stored in scoped pointer to ensure * destruction before other QObject auto-cleanup as it * manage sub objects on its own that have this view as parent */ QScopedPointer m_scriptActionMenu; }; } #endif diff --git a/src/view/kateviewaccessible.h b/src/view/kateviewaccessible.h index e2903c06..226c6f0c 100644 --- a/src/view/kateviewaccessible.h +++ b/src/view/kateviewaccessible.h @@ -1,247 +1,247 @@ /* This file is part of the KDE libraries Copyright (C) 2010 Sebastian Sauer Copyright (C) 2012 Frederik Gladhorn 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_VIEW_ACCESSIBLE_ #define _KATE_VIEW_ACCESSIBLE_ #ifndef QT_NO_ACCESSIBILITY #include "kateviewinternal.h" #include "katedocument.h" #include #include #include /** * This class implements a QAccessible-interface for a KateViewInternal. * * This is the root class for the kateview. The \a KateCursorAccessible class * represents the cursor in the kateview and is a child of this class. */ class KateViewAccessible : public QAccessibleWidget, public QAccessibleTextInterface // FIXME maybe:, public QAccessibleEditableTextInterface { public: explicit KateViewAccessible(KateViewInternal *view) : QAccessibleWidget(view, QAccessible::EditableText) { } void *interface_cast(QAccessible::InterfaceType t) Q_DECL_OVERRIDE { if (t == QAccessible::TextInterface) return static_cast(this); return nullptr; } - virtual ~KateViewAccessible() + ~KateViewAccessible() Q_DECL_OVERRIDE { } QAccessibleInterface *childAt(int x, int y) const Q_DECL_OVERRIDE { Q_UNUSED(x); Q_UNUSED(y); return nullptr; } void setText(QAccessible::Text t, const QString &text) Q_DECL_OVERRIDE { if (t == QAccessible::Value && view()->view()->document()) { view()->view()->document()->setText(text); } } QAccessible::State state() const Q_DECL_OVERRIDE { QAccessible::State s = QAccessibleWidget::state(); s.focusable = view()->focusPolicy() != Qt::NoFocus; s.focused = view()->hasFocus(); s.editable = true; s.multiLine = true; s.selectableText = true; return s; } QString text(QAccessible::Text t) const Q_DECL_OVERRIDE { QString s; if (view()->view()->document()) { if (t == QAccessible::Name) { s = view()->view()->document()->documentName(); } if (t == QAccessible::Value) { s = view()->view()->document()->text(); } } return s; } int characterCount() const Q_DECL_OVERRIDE { return view()->view()->document()->text().size(); } void addSelection(int startOffset, int endOffset) Q_DECL_OVERRIDE { KTextEditor::Range range; range.setRange(cursorFromInt(startOffset), cursorFromInt(endOffset)); view()->view()->setSelection(range); view()->view()->setCursorPosition(cursorFromInt(endOffset)); } QString attributes(int offset, int *startOffset, int *endOffset) const Q_DECL_OVERRIDE { Q_UNUSED(offset); *startOffset = 0; *endOffset = characterCount(); return QString(); } QRect characterRect(int offset) const Q_DECL_OVERRIDE { KTextEditor::Cursor c = cursorFromInt(offset); if (!c.isValid()) { return QRect(); } QPoint p = view()->cursorToCoordinate(c); KTextEditor::Cursor endCursor = KTextEditor::Cursor(c.line(), c.column() + 1); QPoint size = view()->cursorToCoordinate(endCursor) - p; return QRect(view()->mapToGlobal(p), QSize(size.x(), size.y())); } int cursorPosition() const Q_DECL_OVERRIDE { KTextEditor::Cursor c = view()->getCursor(); return positionFromCursor(view(), c); } int offsetAtPoint(const QPoint & /*point*/) const Q_DECL_OVERRIDE { return 0; } void removeSelection(int selectionIndex) Q_DECL_OVERRIDE { if (selectionIndex != 0) { return; } view()->view()->clearSelection(); } void scrollToSubstring(int /*startIndex*/, int /*endIndex*/) Q_DECL_OVERRIDE { // FIXME } void selection(int selectionIndex, int *startOffset, int *endOffset) const Q_DECL_OVERRIDE { if (selectionIndex != 0 || !view()->view()->selection()) { *startOffset = 0; *endOffset = 0; return; } KTextEditor::Range range = view()->view()->selectionRange(); *startOffset = positionFromCursor(view(), range.start()); *endOffset = positionFromCursor(view(), range.end()); } int selectionCount() const Q_DECL_OVERRIDE { return view()->view()->selection() ? 1 : 0; } void setCursorPosition(int position) Q_DECL_OVERRIDE { view()->view()->setCursorPosition(cursorFromInt(position)); } void setSelection(int selectionIndex, int startOffset, int endOffset) Q_DECL_OVERRIDE { if (selectionIndex != 0) { return; } KTextEditor::Range range = KTextEditor::Range(cursorFromInt(startOffset), cursorFromInt(endOffset)); view()->view()->setSelection(range); } QString text(int startOffset, int endOffset) const Q_DECL_OVERRIDE { if (startOffset > endOffset) { return QString(); } return view()->view()->document()->text().mid(startOffset, endOffset - startOffset); } static int positionFromCursor(KateViewInternal *view, const KTextEditor::Cursor &cursor) { int pos = 0; for (int line = 0; line < cursor.line(); ++line) { // length of the line plus newline pos += view->view()->document()->line(line).size() + 1; } pos += cursor.column(); return pos; } private: inline KateViewInternal *view() const { return static_cast(object()); } KTextEditor::Cursor cursorFromInt(int position) const { int line = 0; for (;;) { const QString lineString = view()->view()->document()->line(line); if (position > lineString.length()) { // one is the newline position -= lineString.length() + 1; ++line; } else { break; } } return KTextEditor::Cursor(line, position); } QString textLine(int shiftLines, int offset, int *startOffset, int *endOffset) const { KTextEditor::Cursor pos = cursorFromInt(offset); pos.setColumn(0); if (shiftLines) { pos.setLine(pos.line() + shiftLines); } *startOffset = positionFromCursor(view(), pos); QString line = view()->view()->document()->line(pos.line()) + QLatin1Char('\n'); *endOffset = *startOffset + line.length(); return line; } }; /** * Factory-function used to create \a KateViewAccessible instances for KateViewInternal * to make the KateViewInternal accessible. */ QAccessibleInterface *accessibleInterfaceFactory(const QString &key, QObject *object) { Q_UNUSED(key) //if (key == QLatin1String("KateViewInternal")) if (KateViewInternal *view = qobject_cast(object)) { return new KateViewAccessible(view); } return nullptr; } #endif #endif diff --git a/src/view/kateviewhelpers.cpp b/src/view/kateviewhelpers.cpp index 4121259e..255c21e9 100644 --- a/src/view/kateviewhelpers.cpp +++ b/src/view/kateviewhelpers.cpp @@ -1,2885 +1,2885 @@ /* This file is part of the KDE libraries Copyright (C) 2008, 2009 Matthew Woehlke Copyright (C) 2007 Mirko Stocker Copyright (C) 2002 John Firebaugh Copyright (C) 2001 Anders Lund Copyright (C) 2001 Christoph Cullmann Copyright (C) 2011 Svyatoslav Kuzmich Copyright (C) 2012 KÃ¥re Särs (Minimap) 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 "kateviewhelpers.h" #include "katecmd.h" #include #include #include #include "kateconfig.h" #include "katedocument.h" #include #include "katerenderer.h" #include "kateview.h" #include "kateviewinternal.h" #include "katelayoutcache.h" #include "katetextlayout.h" #include "kateglobal.h" #include "katepartdebug.h" #include "katecommandrangeexpressionparser.h" #include "kateabstractinputmode.h" #include "katetextpreview.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 //BEGIN KateMessageLayout KateMessageLayout::KateMessageLayout(QWidget *parent) : QLayout(parent) { } KateMessageLayout::~KateMessageLayout() { while (QLayoutItem *item = takeAt(0)) delete item; } void KateMessageLayout::addItem(QLayoutItem *item) { Q_ASSERT(false); add(item, KTextEditor::Message::CenterInView); } void KateMessageLayout::addWidget(QWidget *widget, KTextEditor::Message::MessagePosition pos) { add(new QWidgetItem(widget), pos); } int KateMessageLayout::count() const { return m_items.size(); } QLayoutItem *KateMessageLayout::itemAt(int index) const { if (index < 0 || index >= m_items.size()) - return 0; + return nullptr; return m_items[index]->item; } void KateMessageLayout::setGeometry(const QRect &rect) { QLayout::setGeometry(rect); const int s = spacing(); const QRect adjustedRect = rect.adjusted(s, s, -s, -s); for (auto wrapper : m_items) { QLayoutItem *item = wrapper->item; auto position = wrapper->position; if (position == KTextEditor::Message::TopInView) { const QRect r(adjustedRect.width() - item->sizeHint().width(), s, item->sizeHint().width(), item->sizeHint().height()); item->setGeometry(r); } else if (position == KTextEditor::Message::BottomInView) { const QRect r(adjustedRect.width() - item->sizeHint().width(), adjustedRect.height() - item->sizeHint().height(), item->sizeHint().width(), item->sizeHint().height()); item->setGeometry(r); } else if (position == KTextEditor::Message::CenterInView) { QRect r(0, 0, item->sizeHint().width(), item->sizeHint().height()); r.moveCenter(adjustedRect.center()); item->setGeometry(r); } else { Q_ASSERT_X(false, "setGeometry", "Only TopInView, CenterInView, and BottomInView are supported."); } } } QSize KateMessageLayout::sizeHint() const { return QSize(); } QLayoutItem *KateMessageLayout::takeAt(int index) { if (index >= 0 && index < m_items.size()) { ItemWrapper *layoutStruct = m_items.takeAt(index); return layoutStruct->item; } return 0; } void KateMessageLayout::add(QLayoutItem *item, KTextEditor::Message::MessagePosition pos) { m_items.push_back(new ItemWrapper(item, pos)); } //END KateMessageLayout //BEGIN KateScrollBar static const int s_lineWidth = 100; static const int s_pixelMargin = 8; static const int s_linePixelIncLimit = 6; const unsigned char KateScrollBar::characterOpacity[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // <- 15 0, 0, 0, 0, 0, 0, 0, 0, 255, 0, 255, 0, 0, 0, 0, 0, // <- 31 0, 125, 41, 221, 138, 195, 218, 21, 142, 142, 137, 137, 97, 87, 87, 140, // <- 47 223, 164, 183, 190, 191, 193, 214, 158, 227, 216, 103, 113, 146, 140, 146, 149, // <- 63 248, 204, 240, 174, 217, 197, 178, 205, 209, 176, 168, 211, 160, 246, 238, 218, // <- 79 195, 229, 227, 196, 167, 212, 188, 238, 197, 169, 189, 158, 21, 151, 115, 90, // <- 95 15, 192, 209, 153, 208, 187, 162, 221, 183, 149, 161, 191, 146, 203, 167, 182, // <- 111 208, 203, 139, 166, 158, 167, 157, 189, 164, 179, 156, 167, 145, 166, 109, 0, // <- 127 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // <- 143 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // <- 159 0, 125, 184, 187, 146, 201, 127, 203, 89, 194, 156, 141, 117, 87, 202, 88, // <- 175 115, 165, 118, 121, 85, 190, 236, 87, 88, 111, 151, 140, 194, 191, 203, 148, // <- 191 215, 215, 222, 224, 223, 234, 230, 192, 208, 208, 216, 217, 187, 187, 194, 195, // <- 207 228, 255, 228, 228, 235, 239, 237, 150, 255, 222, 222, 229, 232, 180, 197, 225, // <- 223 208, 208, 216, 217, 212, 230, 218, 170, 202, 202, 211, 204, 156, 156, 165, 159, // <- 239 214, 194, 197, 197, 206, 206, 201, 132, 214, 183, 183, 192, 187, 195, 227, 198 }; KateScrollBar::KateScrollBar(Qt::Orientation orientation, KateViewInternal *parent) : QScrollBar(orientation, parent->m_view) , m_middleMouseDown(false) , m_leftMouseDown(false) , m_view(parent->m_view) , m_doc(parent->doc()) , m_viewInternal(parent) , m_textPreview(nullptr) , m_showMarks(false) , m_showMiniMap(false) , m_miniMapAll(true) , m_miniMapWidth(40) , m_grooveHeight(height()) , m_linesModified(0) { connect(this, SIGNAL(valueChanged(int)), this, SLOT(sliderMaybeMoved(int))); connect(m_doc, SIGNAL(marksChanged(KTextEditor::Document*)), this, SLOT(marksChanged())); m_updateTimer.setInterval(300); m_updateTimer.setSingleShot(true); QTimer::singleShot(10, this, SLOT(updatePixmap())); // track mouse for text preview widget setMouseTracking(orientation == Qt::Vertical); // setup text preview timer m_delayTextPreviewTimer.setSingleShot(true); m_delayTextPreviewTimer.setInterval(250); connect(&m_delayTextPreviewTimer, SIGNAL(timeout()), this, SLOT(showTextPreview())); } KateScrollBar::~KateScrollBar() { delete m_textPreview; } void KateScrollBar::setShowMiniMap(bool b) { if (b && !m_showMiniMap) { connect(m_view, SIGNAL(selectionChanged(KTextEditor::View*)), &m_updateTimer, SLOT(start()), Qt::UniqueConnection); connect(m_doc, SIGNAL(textChanged(KTextEditor::Document*)), &m_updateTimer, SLOT(start()), Qt::UniqueConnection); connect(m_view, SIGNAL(delayedUpdateOfView()), &m_updateTimer, SLOT(start()), Qt::UniqueConnection); connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updatePixmap()), Qt::UniqueConnection); connect(&(m_view->textFolding()), SIGNAL(foldingRangesChanged()), &m_updateTimer, SLOT(start()), Qt::UniqueConnection); } else if (!b) { disconnect(&m_updateTimer); } m_showMiniMap = b; updateGeometry(); update(); } QSize KateScrollBar::sizeHint() const { if (m_showMiniMap) { return QSize(m_miniMapWidth, QScrollBar::sizeHint().height()); } return QScrollBar::sizeHint(); } int KateScrollBar::minimapYToStdY(int y) { // Check if the minimap fills the whole scrollbar if (m_stdGroveRect.height() == m_mapGroveRect.height()) { return y; } // check if y is on the step up/down if ((y < m_stdGroveRect.top()) || (y > m_stdGroveRect.bottom())) { return y; } if (y < m_mapGroveRect.top()) { return m_stdGroveRect.top() + 1; } if (y > m_mapGroveRect.bottom()) { return m_stdGroveRect.bottom() - 1; } // check for div/0 if (m_mapGroveRect.height() == 0) { return y; } int newY = (y - m_mapGroveRect.top()) * m_stdGroveRect.height() / m_mapGroveRect.height(); newY += m_stdGroveRect.top(); return newY; } void KateScrollBar::mousePressEvent(QMouseEvent *e) { // delete text preview hideTextPreview(); if (e->button() == Qt::MidButton) { m_middleMouseDown = true; } else if (e->button() == Qt::LeftButton) { m_leftMouseDown = true; } if (m_showMiniMap) { if (m_leftMouseDown) { // if we show the minimap left-click jumps directly to the selected position int newVal = (e->pos().y()-m_mapGroveRect.top()) / (double)m_mapGroveRect.height() * (double)(maximum()+pageStep()) - pageStep()/2; newVal = qBound(0, newVal, maximum()); setSliderPosition(newVal); } QMouseEvent eMod(QEvent::MouseButtonPress, QPoint(6, minimapYToStdY(e->pos().y())), e->button(), e->buttons(), e->modifiers()); QScrollBar::mousePressEvent(&eMod); } else { QScrollBar::mousePressEvent(e); } m_toolTipPos = e->globalPos() - QPoint(e->pos().x(), 0); const int fromLine = m_viewInternal->toRealCursor(m_viewInternal->startPos()).line() + 1; const int lastLine = m_viewInternal->toRealCursor(m_viewInternal->endPos()).line() + 1; QToolTip::showText(m_toolTipPos, i18nc("from line - to line", "
%1

%2
", fromLine, lastLine), this); redrawMarks(); } void KateScrollBar::mouseReleaseEvent(QMouseEvent *e) { if (e->button() == Qt::MidButton) { m_middleMouseDown = false; } else if (e->button() == Qt::LeftButton) { m_leftMouseDown = false; } redrawMarks(); if (m_leftMouseDown || m_middleMouseDown) { QToolTip::hideText(); } if (m_showMiniMap) { QMouseEvent eMod(QEvent::MouseButtonRelease, QPoint(e->pos().x(), minimapYToStdY(e->pos().y())), e->button(), e->buttons(), e->modifiers()); QScrollBar::mouseReleaseEvent(&eMod); } else { QScrollBar::mouseReleaseEvent(e); } } void KateScrollBar::mouseMoveEvent(QMouseEvent *e) { if (m_showMiniMap) { QMouseEvent eMod(QEvent::MouseMove, QPoint(e->pos().x(), minimapYToStdY(e->pos().y())), e->button(), e->buttons(), e->modifiers()); QScrollBar::mouseMoveEvent(&eMod); } else { QScrollBar::mouseMoveEvent(e); } if (e->buttons() & (Qt::LeftButton | Qt::MidButton)) { redrawMarks(); // current line tool tip m_toolTipPos = e->globalPos() - QPoint(e->pos().x(), 0); const int fromLine = m_viewInternal->toRealCursor(m_viewInternal->startPos()).line() + 1; const int lastLine = m_viewInternal->toRealCursor(m_viewInternal->endPos()).line() + 1; QToolTip::showText(m_toolTipPos, i18nc("from line - to line", "
%1

%2
", fromLine, lastLine), this); } showTextPreviewDelayed(); } void KateScrollBar::leaveEvent(QEvent *event) { hideTextPreview(); QAbstractSlider::leaveEvent(event); } bool KateScrollBar::eventFilter(QObject *object, QEvent *event) { Q_UNUSED(object) if (m_textPreview && event->type() == QEvent::WindowDeactivate) { // We need hide the scrollbar TextPreview widget hideTextPreview(); } return false; } void KateScrollBar::paintEvent(QPaintEvent *e) { if (m_doc->marks().size() != m_lines.size()) { recomputeMarksPositions(); } if (m_showMiniMap) { miniMapPaintEvent(e); } else { normalPaintEvent(e); } } void KateScrollBar::showTextPreviewDelayed() { if (!m_textPreview) { if (!m_delayTextPreviewTimer.isActive()) { m_delayTextPreviewTimer.start(); } } else { showTextPreview(); } } void KateScrollBar::showTextPreview() { if (orientation() != Qt::Vertical || isSliderDown() || (minimum() == maximum()) || !m_view->config()->scrollBarPreview()) { return; } QRect grooveRect; if (m_showMiniMap) { // If mini-map is shown, the height of the map might not be the whole height grooveRect = m_mapGroveRect; } else { QStyleOptionSlider opt; opt.init(this); opt.subControls = QStyle::SC_None; opt.activeSubControls = QStyle::SC_None; opt.orientation = orientation(); opt.minimum = minimum(); opt.maximum = maximum(); opt.sliderPosition = sliderPosition(); opt.sliderValue = value(); opt.singleStep = singleStep(); opt.pageStep = pageStep(); grooveRect = style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarGroove, this); } if (m_view->config()->scrollPastEnd()) { // Adjust the grove size to accommodate the added pageStep at the bottom int adjust = pageStep()*grooveRect.height() / (maximum() + pageStep() - minimum()); grooveRect.adjust(0,0,0, -adjust); } const QPoint cursorPos = mapFromGlobal(QCursor::pos()); if (grooveRect.contains(cursorPos)) { if (!m_textPreview) { m_textPreview = new KateTextPreview(m_view); m_textPreview->setAttribute(Qt::WA_ShowWithoutActivating); m_textPreview->setFrameStyle(QFrame::StyledPanel); // event filter to catch application WindowDeactivate event, to hide the preview window qApp->installEventFilter(this); } const qreal posInPercent = static_cast(cursorPos.y() - grooveRect.top()) / grooveRect.height(); const qreal startLine = posInPercent * m_view->textFolding().visibleLines(); m_textPreview->resize(m_view->width() / 2, m_view->height() / 5); const int xGlobal = mapToGlobal(QPoint(0, 0)).x(); const int yGlobal = qMin(mapToGlobal(QPoint(0, height())).y() - m_textPreview->height(), qMax(mapToGlobal(QPoint(0, 0)).y(), mapToGlobal(cursorPos).y() - m_textPreview->height() / 2)); m_textPreview->move(xGlobal - m_textPreview->width(), yGlobal); m_textPreview->setLine(startLine); m_textPreview->setCenterView(true); m_textPreview->setScaleFactor(0.8); m_textPreview->raise(); m_textPreview->show(); } else { hideTextPreview(); } } void KateScrollBar::hideTextPreview() { if (m_delayTextPreviewTimer.isActive()) { m_delayTextPreviewTimer.stop(); } qApp->removeEventFilter(this); delete m_textPreview; } // This function is optimized for bing called in sequence. const QColor KateScrollBar::charColor(const QVector &attributes, int &attributeIndex, const QList &decorations, const QColor &defaultColor, int x, QChar ch) { QColor color = defaultColor; bool styleFound = false; // Query the decorations, that is, things like search highlighting, or the // KDevelop DUChain highlighting, for a color to use foreach (const QTextLayout::FormatRange &range, decorations) { if (range.start <= x && range.start + range.length > x) { // If there's a different background color set (search markers, ...) // use that, otherwise use the foreground color. if (range.format.hasProperty(QTextFormat::BackgroundBrush)) { color = range.format.background().color(); } else { color = range.format.foreground().color(); } styleFound = true; break; } } // If there's no decoration set for the current character (this will mostly be the case for // plain Kate), query the styles, that is, the default kate syntax highlighting. if (!styleFound) { // go to the block containing x while ((attributeIndex < attributes.size()) && ((attributes[attributeIndex].offset + attributes[attributeIndex].length) < x)) { ++attributeIndex; } if ((attributeIndex < attributes.size()) && (x < attributes[attributeIndex].offset + attributes[attributeIndex].length)) { color = m_view->renderer()->attribute(attributes[attributeIndex].attributeValue)->foreground().color(); } } // Query how much "blackness" the character has. // This causes for example a dot or a dash to appear less intense // than an A or similar. // This gives the pixels created a bit of structure, which makes it look more // like real text. color.setAlpha((ch.unicode() < 256) ? characterOpacity[ch.unicode()] : 222); return color; } void KateScrollBar::updatePixmap() { //QTime time; //time.start(); if (!m_showMiniMap) { // make sure no time is wasted if the option is disabled return; } // For performance reason, only every n-th line will be drawn if the widget is // sufficiently small compared to the amount of lines in the document. int docLineCount = m_view->textFolding().visibleLines(); int pixmapLineCount = docLineCount; if (m_view->config()->scrollPastEnd()) { pixmapLineCount += pageStep(); } int pixmapLinesUnscaled = pixmapLineCount; if (m_grooveHeight < 5) { m_grooveHeight = 5; } int lineDivisor = pixmapLinesUnscaled / m_grooveHeight; if (lineDivisor < 1) { lineDivisor = 1; } int charIncrement = 1; int lineIncrement = 1; if ((m_grooveHeight > 10) && (pixmapLineCount >= m_grooveHeight * 2)) { charIncrement = pixmapLineCount / m_grooveHeight; while (charIncrement > s_linePixelIncLimit) { lineIncrement++; pixmapLineCount = pixmapLinesUnscaled / lineIncrement; charIncrement = pixmapLineCount / m_grooveHeight; } pixmapLineCount /= charIncrement; } int pixmapLineWidth = s_pixelMargin + s_lineWidth / charIncrement; //qCDebug(LOG_KTE) << "l" << lineIncrement << "c" << charIncrement << "d" << lineDivisor; //qCDebug(LOG_KTE) << "pixmap" << pixmapLineCount << pixmapLineWidth << "docLines" << m_view->textFolding().visibleLines() << "height" << m_grooveHeight; const QColor backgroundColor = m_view->defaultStyleAttribute(KTextEditor::dsNormal)->background().color(); const QColor defaultTextColor = m_view->defaultStyleAttribute(KTextEditor::dsNormal)->foreground().color(); const QColor selectionBgColor = m_view->renderer()->config()->selectionColor(); QColor modifiedLineColor = m_view->renderer()->config()->modifiedLineColor(); QColor savedLineColor = m_view->renderer()->config()->savedLineColor(); // move the modified line color away from the background color modifiedLineColor.setHsv(modifiedLineColor.hue(), 255, 255 - backgroundColor.value() / 3); savedLineColor.setHsv(savedLineColor.hue(), 100, 255 - backgroundColor.value() / 3); // increase dimensions by ratio m_pixmap = QPixmap(pixmapLineWidth * m_view->devicePixelRatioF(), pixmapLineCount * m_view->devicePixelRatioF()); m_pixmap.fill(QColor("transparent")); // The text currently selected in the document, to be drawn later. const KTextEditor::Range &selection = m_view->selectionRange(); QPainter painter; if (painter.begin(&m_pixmap)) { // init pen once, afterwards, only change it if color changes to avoid a lot of allocation for setPen painter.setPen(selectionBgColor); // Do not force updates of the highlighting if the document is very large bool simpleMode = m_doc->lines() > 7500; int pixelY = 0; int drawnLines = 0; // Iterate over all visible lines, drawing them. for (int virtualLine = 0; virtualLine < docLineCount; virtualLine += lineIncrement) { int realLineNumber = m_view->textFolding().visibleLineToLine(virtualLine); QString lineText = m_doc->line(realLineNumber); if (!simpleMode) { m_doc->buffer().ensureHighlighted(realLineNumber); } const Kate::TextLine &kateline = m_doc->plainKateTextLine(realLineNumber); const QVector &attributes = kateline->attributesList(); QList< QTextLayout::FormatRange > decorations = m_view->renderer()->decorationsForLine(kateline, realLineNumber); int attributeIndex = 0; // Draw selection if it is on an empty line if (selection.contains(KTextEditor::Cursor(realLineNumber, 0)) && lineText.size() == 0) { if (selectionBgColor != painter.pen().color()) { painter.setPen(selectionBgColor); } painter.drawLine(s_pixelMargin, pixelY, s_pixelMargin + s_lineWidth - 1, pixelY); } // Iterate over the line to draw the background int selStartX = -1; int selEndX = -1; int pixelX = s_pixelMargin; // use this to control the offset of the text from the left for (int x = 0; (x < lineText.size() && x < s_lineWidth); x += charIncrement) { if (pixelX >= s_lineWidth + s_pixelMargin) { break; } // Query the selection and draw it behind the character if (selection.contains(KTextEditor::Cursor(realLineNumber, x))) { if (selStartX == -1) selStartX = pixelX; selEndX = pixelX; if (lineText.size() - 1 == x) { selEndX = s_lineWidth + s_pixelMargin-1; } } if (lineText[x] == QLatin1Char('\t')) { pixelX += qMax(4 / charIncrement, 1); // FIXME: tab width... } else { pixelX++; } } if (selStartX != -1) { if (selectionBgColor != painter.pen().color()) { painter.setPen(selectionBgColor); } painter.drawLine(selStartX, pixelY, selEndX, pixelY); } // Iterate over all the characters in the current line pixelX = s_pixelMargin; for (int x = 0; (x < lineText.size() && x < s_lineWidth); x += charIncrement) { if (pixelX >= s_lineWidth + s_pixelMargin) { break; } // draw the pixels if (lineText[x] == QLatin1Char(' ')) { pixelX++; } else if (lineText[x] == QLatin1Char('\t')) { pixelX += qMax(4 / charIncrement, 1); // FIXME: tab width... } else { const QColor newPenColor(charColor(attributes, attributeIndex, decorations, defaultTextColor, x, lineText[x])); if (newPenColor != painter.pen().color()) { painter.setPen(newPenColor); } // Actually draw the pixel with the color queried from the renderer. painter.drawPoint(pixelX, pixelY); pixelX++; } } drawnLines++; if (((drawnLines) % charIncrement) == 0) { pixelY++; } } //qCDebug(LOG_KTE) << drawnLines; // Draw line modification marker map. // Disable this if the document is really huge, // since it requires querying every line. if (m_doc->lines() < 50000) { for (int lineno = 0; lineno < docLineCount; lineno++) { int realLineNo = m_view->textFolding().visibleLineToLine(lineno); const Kate::TextLine &line = m_doc->plainKateTextLine(realLineNo); const QColor & col = line->markedAsModified() ? modifiedLineColor : savedLineColor; if (line->markedAsModified() || line->markedAsSavedOnDisk()) { painter.fillRect(2, lineno / lineDivisor, 3, 1, col); } } } // end painting painter.end(); } // set right ratio m_pixmap.setDevicePixelRatio(m_view->devicePixelRatioF()); //qCDebug(LOG_KTE) << time.elapsed(); // Redraw the scrollbar widget with the updated pixmap. update(); } void KateScrollBar::miniMapPaintEvent(QPaintEvent *e) { QScrollBar::paintEvent(e); QPainter painter(this); QStyleOptionSlider opt; opt.init(this); opt.subControls = QStyle::SC_None; opt.activeSubControls = QStyle::SC_None; opt.orientation = orientation(); opt.minimum = minimum(); opt.maximum = maximum(); opt.sliderPosition = sliderPosition(); opt.sliderValue = value(); opt.singleStep = singleStep(); opt.pageStep = pageStep(); QRect grooveRect = style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarGroove, this); m_stdGroveRect = grooveRect; if (style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarSubLine, this).height() == 0) { int alignMargin = style()->pixelMetric(QStyle::PM_FocusFrameVMargin, &opt, this); grooveRect.moveTop(alignMargin); grooveRect.setHeight(grooveRect.height() - alignMargin); } if (style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarAddLine, this).height() == 0) { int alignMargin = style()->pixelMetric(QStyle::PM_FocusFrameVMargin, &opt, this); grooveRect.setHeight(grooveRect.height() - alignMargin); } m_grooveHeight = grooveRect.height(); const int docXMargin = 1; QRect sliderRect = style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarSlider, this); sliderRect.adjust(docXMargin, 0, 0, 0); //style()->drawControl(QStyle::CE_ScrollBarAddLine, &opt, &painter, this); //style()->drawControl(QStyle::CE_ScrollBarSubLine, &opt, &painter, this); // calculate the document size and position const int docHeight = qMin(grooveRect.height(), int(m_pixmap.height() / m_pixmap.devicePixelRatio() * 2)) - 2 * docXMargin; const int yoffset = 1; // top-aligned in stead of center-aligned (grooveRect.height() - docHeight) / 2; const QRect docRect(QPoint(grooveRect.left() + docXMargin, yoffset + grooveRect.top()), QSize(grooveRect.width() - docXMargin, docHeight)); m_mapGroveRect = docRect; // calculate the visible area int max = qMax(maximum() + 1, 1); int visibleStart = value() * docHeight / (max + pageStep()) + docRect.top() + 0.5; int visibleEnd = (value() + pageStep()) * docHeight / (max + pageStep()) + docRect.top(); QRect visibleRect = docRect; visibleRect.moveTop(visibleStart); visibleRect.setHeight(visibleEnd - visibleStart); // calculate colors const QColor backgroundColor = m_view->defaultStyleAttribute(KTextEditor::dsNormal)->background().color(); const QColor foregroundColor = m_view->defaultStyleAttribute(KTextEditor::dsNormal)->foreground().color(); const QColor highlightColor = palette().link().color(); const int backgroundLightness = backgroundColor.lightness(); const int foregroundLightness = foregroundColor.lightness(); const int lighnessDiff = (foregroundLightness - backgroundLightness); // get a color suited for the color theme QColor darkShieldColor = palette().color(QPalette::Mid); int hue, sat, light; darkShieldColor.getHsl(&hue, &sat, &light); // apply suitable lightness darkShieldColor.setHsl(hue, sat, backgroundLightness + lighnessDiff * 0.35); // gradient for nicer results QLinearGradient gradient(0, 0, width(), 0); gradient.setColorAt(0, darkShieldColor); gradient.setColorAt(0.3, darkShieldColor.lighter(115)); gradient.setColorAt(1, darkShieldColor); QColor lightShieldColor; lightShieldColor.setHsl(hue, sat, backgroundLightness + lighnessDiff * 0.15); QColor outlineColor; outlineColor.setHsl(hue, sat, backgroundLightness + lighnessDiff * 0.5); // draw the grove background in case the document is small painter.setPen(Qt::NoPen); painter.setBrush(backgroundColor); painter.drawRect(grooveRect); // adjust the rectangles if ((docHeight + 2 * docXMargin >= grooveRect.height()) && (sliderRect.height() > visibleRect.height() + 2)) { visibleRect.adjust(2, 0, -3, 0); } else { visibleRect.adjust(1, 0, -1, 2); sliderRect.setTop(visibleRect.top() - 1); sliderRect.setBottom(visibleRect.bottom() + 1); } // Smooth transform only when squeezing if (grooveRect.height() < m_pixmap.height() / m_pixmap.devicePixelRatio()) { painter.setRenderHint(QPainter::SmoothPixmapTransform); } // draw the modified lines margin QRect pixmapMarginRect(QPoint(0, 0), QSize(s_pixelMargin, m_pixmap.height() / m_pixmap.devicePixelRatio())); QRect docPixmapMarginRect(QPoint(0, docRect.top()), QSize(s_pixelMargin, docRect.height())); painter.drawPixmap(docPixmapMarginRect, m_pixmap, pixmapMarginRect); // calculate the stretch and draw the stretched lines (scrollbar marks) QRect pixmapRect(QPoint(s_pixelMargin, 0), QSize(m_pixmap.width() / m_pixmap.devicePixelRatio() - s_pixelMargin, m_pixmap.height() / m_pixmap.devicePixelRatio())); QRect docPixmapRect(QPoint(s_pixelMargin, docRect.top()), QSize(docRect.width() - s_pixelMargin, docRect.height())); painter.drawPixmap(docPixmapRect, m_pixmap, pixmapRect); // delimit the end of the document const int y = docPixmapRect.height() + grooveRect.y(); if (y+2 < grooveRect.y() + grooveRect.height()) { QColor fg(foregroundColor); fg.setAlpha(30); painter.setBrush(Qt::NoBrush); painter.setPen(QPen(fg, 1)); painter.drawLine(grooveRect.x()+1,y+2,width()-1,y+2); } // fade the invisible sections const QRect top( grooveRect.x(), grooveRect.y(), grooveRect.width(), visibleRect.y()-grooveRect.y() //Pen width ); const QRect bottom( grooveRect.x(), grooveRect.y()+visibleRect.y()+visibleRect.height()-grooveRect.y(), //Pen width grooveRect.width(), grooveRect.height() - (visibleRect.y()-grooveRect.y())-visibleRect.height() ); QColor faded(backgroundColor); faded.setAlpha(110); painter.fillRect(top, faded); painter.fillRect(bottom, faded); // add a thin line to limit the scrollbar QColor c(foregroundColor); c.setAlpha(10); painter.setPen(QPen(c,1)); painter.drawLine(0, 0, 0, height()); if (m_showMarks) { QHashIterator it = m_lines; QPen penBg; penBg.setWidth(4); lightShieldColor.setAlpha(180); penBg.setColor(lightShieldColor); painter.setPen(penBg); while (it.hasNext()) { it.next(); int y = (it.key() - grooveRect.top()) * docHeight / grooveRect.height() + docRect.top();; painter.drawLine(6, y, width() - 6, y); } it = m_lines; QPen pen; pen.setWidth(2); while (it.hasNext()) { it.next(); pen.setColor(it.value()); painter.setPen(pen); int y = (it.key() - grooveRect.top()) * docHeight / grooveRect.height() + docRect.top(); painter.drawLine(6, y, width() - 6, y); } } // slider outline QColor sliderColor(highlightColor); sliderColor.setAlpha(50); painter.fillRect(sliderRect, sliderColor); painter.setPen(QPen(highlightColor, 0)); // rounded rect looks ugly for some reason, so we draw 4 lines. painter.drawLine(sliderRect.left(), sliderRect.top() + 1, sliderRect.left(), sliderRect.bottom() - 1); painter.drawLine(sliderRect.right(), sliderRect.top() + 1, sliderRect.right(), sliderRect.bottom() - 1); painter.drawLine(sliderRect.left() + 1, sliderRect.top(), sliderRect.right() - 1, sliderRect.top()); painter.drawLine(sliderRect.left() + 1, sliderRect.bottom(), sliderRect.right() - 1, sliderRect.bottom()); } void KateScrollBar::normalPaintEvent(QPaintEvent *e) { QScrollBar::paintEvent(e); if (!m_showMarks) { return; } QPainter painter(this); QStyleOptionSlider opt; opt.init(this); opt.subControls = QStyle::SC_None; opt.activeSubControls = QStyle::SC_None; opt.orientation = orientation(); opt.minimum = minimum(); opt.maximum = maximum(); opt.sliderPosition = sliderPosition(); opt.sliderValue = value(); opt.singleStep = singleStep(); opt.pageStep = pageStep(); QRect rect = style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarSlider, this); int sideMargin = width() - rect.width(); if (sideMargin < 4) { sideMargin = 4; } sideMargin /= 2; QHashIterator it = m_lines; while (it.hasNext()) { it.next(); painter.setPen(it.value()); if (it.key() < rect.top() || it.key() > rect.bottom()) { painter.drawLine(0, it.key(), width(), it.key()); } else { painter.drawLine(0, it.key(), sideMargin, it.key()); painter.drawLine(width() - sideMargin, it.key(), width(), it.key()); } } } void KateScrollBar::resizeEvent(QResizeEvent *e) { QScrollBar::resizeEvent(e); m_updateTimer.start(); m_lines.clear(); update(); } void KateScrollBar::sliderChange(SliderChange change) { // call parents implementation QScrollBar::sliderChange(change); if (change == QAbstractSlider::SliderValueChange) { redrawMarks(); } else if (change == QAbstractSlider::SliderRangeChange) { marksChanged(); } if (m_leftMouseDown || m_middleMouseDown) { const int fromLine = m_viewInternal->toRealCursor(m_viewInternal->startPos()).line() + 1; const int lastLine = m_viewInternal->toRealCursor(m_viewInternal->endPos()).line() + 1; QToolTip::showText(m_toolTipPos, i18nc("from line - to line", "
%1

%2
", fromLine, lastLine), this); } } void KateScrollBar::marksChanged() { m_lines.clear(); update(); } void KateScrollBar::redrawMarks() { if (!m_showMarks) { return; } update(); } void KateScrollBar::recomputeMarksPositions() { // get the style options to compute the scrollbar pixels QStyleOptionSlider opt; initStyleOption(&opt); QRect grooveRect = style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarGroove, this); // cache top margin and groove height const int top = grooveRect.top(); const int h = grooveRect.height() - 1; // make sure we have a sane height if (h <= 0) { return; } // get total visible (=without folded) lines in the document int visibleLines = m_view->textFolding().visibleLines() - 1; if (m_view->config()->scrollPastEnd()) { visibleLines += m_viewInternal->linesDisplayed() - 1; visibleLines -= m_view->config()->autoCenterLines(); } // now repopulate the scrollbar lines list m_lines.clear(); const QHash &marks = m_doc->marks(); for (QHash::const_iterator i = marks.constBegin(); i != marks.constEnd(); ++i) { KTextEditor::Mark *mark = i.value(); const int line = m_view->textFolding().lineToVisibleLine(mark->line); const double ratio = static_cast(line) / visibleLines; m_lines.insert(top + (int)(h * ratio), KateRendererConfig::global()->lineMarkerColor((KTextEditor::MarkInterface::MarkTypes)mark->type)); } } void KateScrollBar::sliderMaybeMoved(int value) { if (m_middleMouseDown) { // we only need to emit this signal once, as for the following slider // movements the signal sliderMoved() is already emitted. // Thus, set m_middleMouseDown to false right away. m_middleMouseDown = false; emit sliderMMBMoved(value); } } //END //BEGIN KateCmdLineEditFlagCompletion /** * This class provide completion of flags. It shows a short description of * each flag, and flags are appended. */ class KateCmdLineEditFlagCompletion : public KCompletion { public: KateCmdLineEditFlagCompletion() { ; } QString makeCompletion(const QString & /*s*/) Q_DECL_OVERRIDE { return QString(); } }; //END KateCmdLineEditFlagCompletion //BEGIN KateCmdLineEdit KateCommandLineBar::KateCommandLineBar(KTextEditor::ViewPrivate *view, QWidget *parent) : KateViewBarWidget(true, parent) { QHBoxLayout *topLayout = new QHBoxLayout(); centralWidget()->setLayout(topLayout); topLayout->setMargin(0); m_lineEdit = new KateCmdLineEdit(this, view); connect(m_lineEdit, SIGNAL(hideRequested()), SIGNAL(hideMe())); topLayout->addWidget(m_lineEdit); QToolButton *helpButton = new QToolButton(this); helpButton->setAutoRaise(true); helpButton->setIcon(QIcon::fromTheme(QStringLiteral("help-contextual"))); topLayout->addWidget(helpButton); connect(helpButton, SIGNAL(clicked()), this, SLOT(showHelpPage())); setFocusProxy(m_lineEdit); } void KateCommandLineBar::showHelpPage() { KHelpClient::invokeHelp(QStringLiteral("advanced-editing-tools-commandline"), QStringLiteral("kate")); } KateCommandLineBar::~KateCommandLineBar() { } // inserts the given string in the command line edit and (if selected = true) selects it so the user // can type over it if they want to void KateCommandLineBar::setText(const QString &text, bool selected) { m_lineEdit->setText(text); if (selected) { m_lineEdit->selectAll(); } } void KateCommandLineBar::execute(const QString &text) { m_lineEdit->slotReturnPressed(text); } KateCmdLineEdit::KateCmdLineEdit(KateCommandLineBar *bar, KTextEditor::ViewPrivate *view) : KLineEdit() , m_view(view) , m_bar(bar) , m_msgMode(false) , m_histpos(0) , m_cmdend(0) , m_command(nullptr) { connect(this, SIGNAL(returnPressed(QString)), this, SLOT(slotReturnPressed(QString))); setCompletionObject(KateCmd::self()->commandCompletionObject()); setAutoDeleteCompletionObject(false); m_hideTimer = new QTimer(this); m_hideTimer->setSingleShot(true); connect(m_hideTimer, SIGNAL(timeout()), this, SLOT(hideLineEdit())); // make sure the timer is stopped when the user switches views. if not, focus will be given to the // wrong view when KateViewBar::hideCurrentBarWidget() is called after 4 seconds. (the timer is // used for showing things like "Success" for four seconds after the user has used the kate // command line) connect(m_view, SIGNAL(focusOut(KTextEditor::View*)), m_hideTimer, SLOT(stop())); } void KateCmdLineEdit::hideEvent(QHideEvent *e) { Q_UNUSED(e); } QString KateCmdLineEdit::helptext(const QPoint &) const { QString beg = QStringLiteral("
Help: "); QString mid = QStringLiteral("
"); QString end = QStringLiteral("
"); QString t = text(); QRegExp re(QLatin1String("\\s*help\\s+(.*)")); if (re.indexIn(t) > -1) { QString s; // get help for command QString name = re.cap(1); if (name == QLatin1String("list")) { return beg + i18n("Available Commands") + mid + KateCmd::self()->commandList().join(QLatin1Char(' ')) + i18n("

For help on individual commands, do 'help <command>'

") + end; } else if (! name.isEmpty()) { KTextEditor::Command *cmd = KateCmd::self()->queryCommand(name); if (cmd) { if (cmd->help(m_view, name, s)) { return beg + name + mid + s + end; } else { return beg + name + mid + i18n("No help for '%1'", name) + end; } } else { return beg + mid + i18n("No such command %1", name) + end; } } } return beg + mid + i18n( "

This is the Katepart command line.
" "Syntax: command [ arguments ]
" "For a list of available commands, enter help list
" "For help for individual commands, enter help <command>

") + end; } bool KateCmdLineEdit::event(QEvent *e) { if (e->type() == QEvent::QueryWhatsThis) { setWhatsThis(helptext(QPoint())); e->accept(); return true; } return KLineEdit::event(e); } /** * Parse the text as a command. * * The following is a simple PEG grammar for the syntax of the command. * (A PEG grammar is like a BNF grammar, except that "/" stands for * ordered choice: only the first matching rule is used. In other words, * the parsing is short-circuited in the manner of the "or" operator in * programming languages, and so the grammar is unambiguous.) * * Text <- Range? Command * / Position * Range <- Position ("," Position)? * / "%" * Position <- Base Offset? * Base <- Line * / LastLine * / ThisLine * / Mark * Offset <- [+-] Base * Line <- [0-9]+ * LastLine <- "$" * ThisLine <- "." * Mark <- "'" [a-z] */ void KateCmdLineEdit::slotReturnPressed(const QString &text) { if (text.isEmpty()) { return; } // silently ignore leading space characters uint n = 0; const uint textlen = text.length(); while ((n < textlen) && (text[n].isSpace())) { n++; } if (n >= textlen) { return; } QString cmd = text.mid(n); // Parse any leading range expression, and strip it (and maybe do some other transforms on the command). QString leadingRangeExpression; KTextEditor::Range range = CommandRangeExpressionParser::parseRangeExpression(cmd, m_view, leadingRangeExpression, cmd); // Built in help: if the command starts with "help", [try to] show some help if (cmd.startsWith(QLatin1String("help"))) { QWhatsThis::showText(mapToGlobal(QPoint(0, 0)), helptext(QPoint())); clear(); KateCmd::self()->appendHistory(cmd); m_histpos = KateCmd::self()->historyLength(); m_oldText.clear(); return; } if (cmd.length() > 0) { KTextEditor::Command *p = KateCmd::self()->queryCommand(cmd); m_oldText = leadingRangeExpression + cmd; m_msgMode = true; // the following commands changes the focus themselves, so bar should be hidden before execution. if (QRegExp(QLatin1String("buffer|b|new|vnew|bp|bprev|bn|bnext|bf|bfirst|bl|blast|edit|e")).exactMatch(cmd.split(QLatin1Char(' ')).at(0))) { emit hideRequested(); } if (!p) { setText(i18n("No such command: \"%1\"", cmd)); } else if (range.isValid() && !p->supportsRange(cmd)) { // Raise message, when the command does not support ranges. setText(i18n("Error: No range allowed for command \"%1\".", cmd)); } else { QString msg; if (p->exec(m_view, cmd, msg, range)) { // append command along with range (will be empty if none given) to history KateCmd::self()->appendHistory(leadingRangeExpression + cmd); m_histpos = KateCmd::self()->historyLength(); m_oldText.clear(); if (msg.length() > 0) { setText(i18n("Success: ") + msg); } else if (isVisible()) { // always hide on success without message emit hideRequested(); } } else { if (msg.length() > 0) { if (msg.contains(QLatin1Char('\n'))) { // multiline error, use widget with more space QWhatsThis::showText(mapToGlobal(QPoint(0, 0)), msg); } else { setText(msg); } } else { setText(i18n("Command \"%1\" failed.", cmd)); } } } } // clean up if (completionObject() != KateCmd::self()->commandCompletionObject()) { KCompletion *c = completionObject(); setCompletionObject(KateCmd::self()->commandCompletionObject()); delete c; } m_command = nullptr; m_cmdend = 0; // the following commands change the focus themselves if (!QRegExp(QLatin1String("buffer|b|new|vnew|bp|bprev|bn|bnext|bf|bfirst|bl|blast|edit|e")).exactMatch(cmd.split(QLatin1Char(' ')).at(0))) { m_view->setFocus(); } if (isVisible()) { m_hideTimer->start(4000); } } void KateCmdLineEdit::hideLineEdit() // unless i have focus ;) { if (! hasFocus()) { emit hideRequested(); } } void KateCmdLineEdit::focusInEvent(QFocusEvent *ev) { if (m_msgMode) { m_msgMode = false; setText(m_oldText); selectAll(); } KLineEdit::focusInEvent(ev); } void KateCmdLineEdit::keyPressEvent(QKeyEvent *ev) { if (ev->key() == Qt::Key_Escape || (ev->key() == Qt::Key_BracketLeft && ev->modifiers() == Qt::ControlModifier)) { m_view->setFocus(); hideLineEdit(); clear(); } else if (ev->key() == Qt::Key_Up) { fromHistory(true); } else if (ev->key() == Qt::Key_Down) { fromHistory(false); } uint cursorpos = cursorPosition(); KLineEdit::keyPressEvent(ev); // during typing, let us see if we have a valid command if (! m_cmdend || cursorpos <= m_cmdend) { QChar c; if (! ev->text().isEmpty()) { c = ev->text().at(0); } if (! m_cmdend && ! c.isNull()) { // we have no command, so lets see if we got one if (! c.isLetterOrNumber() && c != QLatin1Char('-') && c != QLatin1Char('_')) { m_command = KateCmd::self()->queryCommand(text().trimmed()); if (m_command) { //qCDebug(LOG_KTE)<<"keypress in commandline: We have a command! "<queryCommand(text().trimmed()); if (m_command) { //qCDebug(LOG_KTE)<<"keypress in commandline: We have a command! "<commandCompletionObject()) { KCompletion *c = completionObject(); setCompletionObject(KateCmd::self()->commandCompletionObject()); delete c; } m_cmdend = 0; } } // if we got a command, check if it wants to do something. if (m_command) { KCompletion *cmpl = m_command->completionObject(m_view, text().left(m_cmdend).trimmed()); if (cmpl) { // We need to prepend the current command name + flag string // when completion is done //qCDebug(LOG_KTE)<<"keypress in commandline: Setting completion object!"; setCompletionObject(cmpl); } } } else if (m_command && !ev->text().isEmpty()) { // check if we should call the commands processText() if (m_command->wantsToProcessText(text().left(m_cmdend).trimmed())) { m_command->processText(m_view, text()); } } } void KateCmdLineEdit::fromHistory(bool up) { if (! KateCmd::self()->historyLength()) { return; } QString s; if (up) { if (m_histpos > 0) { m_histpos--; s = KateCmd::self()->fromHistory(m_histpos); } } else { if (m_histpos < (KateCmd::self()->historyLength() - 1)) { m_histpos++; s = KateCmd::self()->fromHistory(m_histpos); } else { m_histpos = KateCmd::self()->historyLength(); setText(m_oldText); } } if (! s.isEmpty()) { // Select the argument part of the command, so that it is easy to overwrite setText(s); static QRegExp reCmd = QRegExp(QLatin1String(".*[\\w\\-]+(?:[^a-zA-Z0-9_-]|:\\w+)(.*)")); if (reCmd.indexIn(text()) == 0) { setSelection(text().length() - reCmd.cap(1).length(), reCmd.cap(1).length()); } } } //END KateCmdLineEdit //BEGIN KateIconBorder using namespace KTextEditor; KateIconBorder::KateIconBorder(KateViewInternal *internalView, QWidget *parent) : QWidget(parent) , m_view(internalView->m_view) , m_doc(internalView->doc()) , m_viewInternal(internalView) , m_iconBorderOn(false) , m_lineNumbersOn(false) , m_relLineNumbersOn(false) , m_updateRelLineNumbers(false) , m_foldingMarkersOn(false) , m_dynWrapIndicatorsOn(false) , m_annotationBorderOn(false) , m_dynWrapIndicators(0) , m_lastClickedLine(-1) , m_cachedLNWidth(0) , m_maxCharWidth(0.0) , iconPaneWidth(16) , m_annotationBorderWidth(6) , m_foldingPreview(nullptr) , m_foldingRange(nullptr) , m_nextHighlightBlock(-2) , m_currentBlockLine(-1) { setAttribute(Qt::WA_StaticContents); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); setMouseTracking(true); m_doc->setMarkDescription(MarkInterface::markType01, i18n("Bookmark")); m_doc->setMarkPixmap(MarkInterface::markType01, QIcon::fromTheme(QStringLiteral("bookmarks")).pixmap(16, 16)); updateFont(); m_delayFoldingHlTimer.setSingleShot(true); m_delayFoldingHlTimer.setInterval(150); connect(&m_delayFoldingHlTimer, SIGNAL(timeout()), this, SLOT(showBlock())); // user interaction (scrolling) hides e.g. preview connect(m_view, SIGNAL(displayRangeChanged(KTextEditor::ViewPrivate*)), this, SLOT(displayRangeChanged())); } KateIconBorder::~KateIconBorder() { delete m_foldingPreview; delete m_foldingRange; } void KateIconBorder::setIconBorderOn(bool enable) { if (enable == m_iconBorderOn) { return; } m_iconBorderOn = enable; updateGeometry(); QTimer::singleShot(0, this, SLOT(update())); } void KateIconBorder::setAnnotationBorderOn(bool enable) { if (enable == m_annotationBorderOn) { return; } m_annotationBorderOn = enable; emit m_view->annotationBorderVisibilityChanged(m_view, enable); updateGeometry(); QTimer::singleShot(0, this, SLOT(update())); } void KateIconBorder::removeAnnotationHovering() { // remove hovering if it's still there if (m_annotationBorderOn && !m_hoveredAnnotationGroupIdentifier.isEmpty()) { m_hoveredAnnotationGroupIdentifier.clear(); hideAnnotationTooltip(); QTimer::singleShot(0, this, SLOT(update())); } } void KateIconBorder::setLineNumbersOn(bool enable) { if (enable == m_lineNumbersOn) { return; } m_lineNumbersOn = enable; m_dynWrapIndicatorsOn = (m_dynWrapIndicators == 1) ? enable : m_dynWrapIndicators; updateGeometry(); QTimer::singleShot(0, this, SLOT(update())); } void KateIconBorder::setRelLineNumbersOn(bool enable) { if (enable == m_relLineNumbersOn) { return; } m_relLineNumbersOn = enable; /* * We don't have to touch the m_dynWrapIndicatorsOn because * we already got it right from the m_lineNumbersOn */ updateGeometry(); QTimer::singleShot( 0, this, SLOT(update()) ); } void KateIconBorder::updateForCursorLineChange() { if (m_relLineNumbersOn) { m_updateRelLineNumbers = true; } // always do normal update, e.g. for different current line color! update(); } void KateIconBorder::setDynWrapIndicators(int state) { if (state == m_dynWrapIndicators) { return; } m_dynWrapIndicators = state; m_dynWrapIndicatorsOn = (state == 1) ? m_lineNumbersOn : state; updateGeometry(); QTimer::singleShot(0, this, SLOT(update())); } void KateIconBorder::setFoldingMarkersOn(bool enable) { if (enable == m_foldingMarkersOn) { return; } m_foldingMarkersOn = enable; updateGeometry(); QTimer::singleShot(0, this, SLOT(update())); } QSize KateIconBorder::sizeHint() const { int w = 0; if (m_iconBorderOn) { w += iconPaneWidth + 2; } if (m_annotationBorderOn) { w += m_annotationBorderWidth + 2; } if (m_lineNumbersOn || (m_view->dynWordWrap() && m_dynWrapIndicatorsOn)) { w += lineNumberWidth() + 2; } if (m_foldingMarkersOn) { w += iconPaneWidth; } // space for the line modification system border if (m_view->config()->lineModification()) { w += 3; } // two pixel space w += 2; return QSize(w, 0); } // This function (re)calculates the maximum width of any of the digit characters (0 -> 9) // for graceful handling of variable-width fonts as the linenumber font. void KateIconBorder::updateFont() { const QFontMetricsF &fm = m_view->renderer()->config()->fontMetrics(); m_maxCharWidth = 0.0; // Loop to determine the widest numeric character in the current font. // 48 is ascii '0' for (int i = 48; i < 58; i++) { const qreal charWidth = ceil(fm.width(QChar(i))); m_maxCharWidth = qMax(m_maxCharWidth, charWidth); } // the icon pane scales with the font... iconPaneWidth = fm.height(); updateGeometry(); QTimer::singleShot(0, this, SLOT(update())); } int KateIconBorder::lineNumberWidth() const { // width = (number of digits + 1) * char width const int digits = (int) ceil(log10((double)(m_view->doc()->lines() + 1))); int width = m_lineNumbersOn ? (int)ceil((digits + 1) * m_maxCharWidth) : 0; if (m_view->dynWordWrap() && m_dynWrapIndicatorsOn) { // HACK: 16 == style().scrollBarExtent().width() width = qMax(16 + 4, width); if (m_cachedLNWidth != width || m_oldBackgroundColor != m_view->renderer()->config()->iconBarColor()) { int w = 16;// HACK: 16 == style().scrollBarExtent().width() style().scrollBarExtent().width(); int h = m_view->renderer()->lineHeight(); QSize newSize(w * devicePixelRatio(), h * devicePixelRatio()); if ((m_arrow.size() != newSize || m_oldBackgroundColor != m_view->renderer()->config()->iconBarColor()) && !newSize.isEmpty()) { m_arrow = QPixmap(newSize); m_arrow.setDevicePixelRatio(devicePixelRatio()); QPainter p(&m_arrow); p.fillRect(0, 0, w, h, m_view->renderer()->config()->iconBarColor()); h = m_view->renderer()->config()->fontMetrics().ascent(); p.setPen(m_view->renderer()->config()->lineNumberColor()); QPainterPath path; path.moveTo(w / 2, h / 2); path.lineTo(w / 2, 0); path.lineTo(w / 4, h / 4); path.lineTo(0, 0); path.lineTo(0, h / 2); path.lineTo(w / 2, h - 1); path.lineTo(w * 3 / 4, h - 1); path.lineTo(w - 1, h * 3 / 4); path.lineTo(w * 3 / 4, h / 2); path.lineTo(0, h / 2); p.drawPath(path); } } } return width; } void KateIconBorder::paintEvent(QPaintEvent *e) { paintBorder(e->rect().x(), e->rect().y(), e->rect().width(), e->rect().height()); } static void paintTriangle(QPainter &painter, QColor c, int xOffset, int yOffset, int width, int height, bool open) { painter.setRenderHint(QPainter::Antialiasing); qreal size = qMin(width, height); if (KColorUtils::luma(c) > 0.25) { c = KColorUtils::darken(c); } else { c = KColorUtils::shade(c, 0.1); } QPen pen; pen.setJoinStyle(Qt::RoundJoin); pen.setColor(c); pen.setWidthF(1.5); painter.setPen(pen); painter.setBrush(c); // let some border, if possible size *= 0.6; qreal halfSize = size / 2; qreal halfSizeP = halfSize * 0.6; QPointF middle(xOffset + (qreal)width / 2, yOffset + (qreal)height / 2); if (open) { QPointF points[3] = { middle + QPointF(-halfSize, -halfSizeP), middle + QPointF(halfSize, -halfSizeP), middle + QPointF(0, halfSizeP) }; painter.drawConvexPolygon(points, 3); } else { QPointF points[3] = { middle + QPointF(-halfSizeP, -halfSize), middle + QPointF(-halfSizeP, halfSize), middle + QPointF(halfSizeP, 0) }; painter.drawConvexPolygon(points, 3); } painter.setRenderHint(QPainter::Antialiasing, false); } void KateIconBorder::paintBorder(int /*x*/, int y, int /*width*/, int height) { uint h = m_view->renderer()->lineHeight(); uint startz = (y / h); uint endz = startz + 1 + (height / h); uint lineRangesSize = m_viewInternal->cache()->viewCacheLineCount(); uint currentLine = m_view->cursorPosition().line(); // center the folding boxes int m_px = (h - 11) / 2; if (m_px < 0) { m_px = 0; } int lnWidth(0); if (m_lineNumbersOn || (m_view->dynWordWrap() && m_dynWrapIndicatorsOn)) { // avoid calculating unless needed ;-) lnWidth = lineNumberWidth(); if (lnWidth != m_cachedLNWidth || m_oldBackgroundColor != m_view->renderer()->config()->iconBarColor()) { // we went from n0 ->n9 lines or vice verca // this causes an extra updateGeometry() first time the line numbers // are displayed, but sizeHint() is supposed to be const so we can't set // the cached value there. m_cachedLNWidth = lnWidth; m_oldBackgroundColor = m_view->renderer()->config()->iconBarColor(); updateGeometry(); update(); return; } } int w(this->width()); // sane value/calc only once QPainter p(this); p.setRenderHints(QPainter::TextAntialiasing); p.setFont(m_view->renderer()->config()->font()); // for line numbers KTextEditor::AnnotationModel *model = m_view->annotationModel() ? m_view->annotationModel() : m_doc->annotationModel(); for (uint z = startz; z <= endz; z++) { int y = h * z; int realLine = -1; if (z < lineRangesSize) { realLine = m_viewInternal->cache()->viewLine(z).line(); } int lnX = 0; p.fillRect(0, y, w - 5, h, m_view->renderer()->config()->iconBarColor()); p.fillRect(w - 5, y, 5, h, m_view->renderer()->config()->backgroundColor()); // icon pane if (m_iconBorderOn) { p.setPen(m_view->renderer()->config()->separatorColor()); p.setBrush(m_view->renderer()->config()->separatorColor()); p.drawLine(lnX + iconPaneWidth + 1, y, lnX + iconPaneWidth + 1, y + h); if ((realLine > -1) && (m_viewInternal->cache()->viewLine(z).startCol() == 0)) { uint mrk(m_doc->mark(realLine)); // call only once if (mrk) { for (uint bit = 0; bit < 32; bit++) { MarkInterface::MarkTypes markType = (MarkInterface::MarkTypes)(1 << bit); if (mrk & markType) { QPixmap px_mark(m_doc->markPixmap(markType)); if (!px_mark.isNull() && h > 0 && iconPaneWidth > 0) { if (iconPaneWidth < px_mark.width() || h < (uint)px_mark.height()) { px_mark = px_mark.scaled(iconPaneWidth, h, Qt::KeepAspectRatio); } // center the mark pixmap int x_px = (iconPaneWidth - px_mark.width()) / 2; if (x_px < 0) { x_px = 0; } int y_px = (h - px_mark.height()) / 2; if (y_px < 0) { y_px = 0; } p.drawPixmap(lnX + x_px, y + y_px, px_mark); } } } } } lnX += iconPaneWidth + 2; } // annotation information if (m_annotationBorderOn) { // Draw a border line between annotations and the line numbers p.setPen(m_view->renderer()->config()->lineNumberColor()); p.setBrush(m_view->renderer()->config()->lineNumberColor()); int borderWidth = m_annotationBorderWidth; p.drawLine(lnX + borderWidth + 1, y, lnX + borderWidth + 1, y + h); if ((realLine > -1) && model) { // Fetch data from the model QVariant text = model->data(realLine, Qt::DisplayRole); QVariant foreground = model->data(realLine, Qt::ForegroundRole); QVariant background = model->data(realLine, Qt::BackgroundRole); // Fill the background if (background.isValid()) { p.fillRect(lnX, y, borderWidth + 1, h, background.value()); } // Set the pen for drawing the foreground if (foreground.isValid()) { p.setPen(foreground.value()); } // Draw a border around all adjacent entries that have the same text as the currently hovered one const QVariant identifier = model->data( realLine, (Qt::ItemDataRole) KTextEditor::AnnotationModel::GroupIdentifierRole ); if( m_hoveredAnnotationGroupIdentifier == identifier.toString() ) { p.drawLine(lnX, y, lnX, y + h); p.drawLine(lnX + borderWidth, y, lnX + borderWidth, y + h); QVariant beforeText = model->data(realLine - 1, Qt::DisplayRole); QVariant afterText = model->data(realLine + 1, Qt::DisplayRole); if (((beforeText.isValid() && beforeText.canConvert() && text.isValid() && text.canConvert() && beforeText.toString() != text.toString()) || realLine == 0) && m_viewInternal->cache()->viewLine(z).viewLine() == 0) { p.drawLine(lnX + 1, y, lnX + borderWidth, y); } if (((afterText.isValid() && afterText.canConvert() && text.isValid() && text.canConvert() && afterText.toString() != text.toString()) || realLine == m_view->doc()->lines() - 1) && m_viewInternal->cache()->viewLine(z).viewLine() == m_viewInternal->cache()->viewLineCount(realLine) - 1) { p.drawLine(lnX + 1, y + h - 1, lnX + borderWidth, y + h - 1); } } if (foreground.isValid()) { QPen pen = p.pen(); pen.setWidth(1); p.setPen(pen); } // Now draw the normal text if (text.isValid() && text.canConvert() && (m_viewInternal->cache()->viewLine(z).startCol() == 0)) { p.drawText(lnX + 3, y, borderWidth - 3, h, Qt::AlignLeft | Qt::AlignVCenter, text.toString()); } } // adjust current X position and reset the pen and brush lnX += borderWidth + 2; } // line number if (m_lineNumbersOn || (m_view->dynWordWrap() && m_dynWrapIndicatorsOn)) { if (realLine > -1) { int distanceToCurrent = abs(realLine - static_cast(currentLine)); QColor color; if (distanceToCurrent == 0) { color = m_view->renderer()->config()->currentLineNumberColor(); } else { color = m_view->renderer()->config()->lineNumberColor(); } p.setPen(color); p.setBrush(color); if (m_viewInternal->cache()->viewLine(z).startCol() == 0) { if (m_relLineNumbersOn) { if (distanceToCurrent == 0) { p.drawText(lnX + m_maxCharWidth / 2, y, lnWidth - m_maxCharWidth, h, Qt::TextDontClip|Qt::AlignLeft|Qt::AlignVCenter, QString::number(realLine + 1)); } else { p.drawText(lnX + m_maxCharWidth / 2, y, lnWidth - m_maxCharWidth, h, Qt::TextDontClip|Qt::AlignRight|Qt::AlignVCenter, QString::number(distanceToCurrent)); } if (m_updateRelLineNumbers) { m_updateRelLineNumbers = false; update(); } } else if (m_lineNumbersOn) { p.drawText(lnX + m_maxCharWidth / 2, y, lnWidth - m_maxCharWidth, h, Qt::TextDontClip | Qt::AlignRight | Qt::AlignVCenter, QString::number(realLine + 1)); } } else if (m_view->dynWordWrap() && m_dynWrapIndicatorsOn) { p.drawPixmap(lnX + lnWidth - (m_arrow.width() / m_arrow.devicePixelRatio()) - 2, y, m_arrow); } } lnX += lnWidth + 2; } // folding markers if (m_foldingMarkersOn) { // first icon border background p.fillRect(lnX, y, iconPaneWidth, h, m_view->renderer()->config()->iconBarColor()); // possible additional folding highlighting if ((realLine >= 0) && m_foldingRange && m_foldingRange->overlapsLine(realLine)) { p.save(); // use linear gradient as brush QLinearGradient g(lnX, y, lnX + iconPaneWidth, y); const QColor foldingColor(m_view->renderer()->config()->foldingColor()); g.setColorAt(0, foldingColor); g.setColorAt(0.3, foldingColor.lighter(110)); g.setColorAt(1, foldingColor); p.setBrush(g); p.setPen(foldingColor); p.setClipRect(lnX, y, iconPaneWidth, h); p.setRenderHint(QPainter::Antialiasing); const qreal r = 4.0; if (m_foldingRange->start().line() == realLine && m_viewInternal->cache()->viewLine(z).viewLine() == 0) { p.drawRect(lnX, y, iconPaneWidth, h + r); } else if (m_foldingRange->end().line() == realLine && m_viewInternal->cache()->viewLine(z).viewLine() == m_viewInternal->cache()->viewLineCount(realLine) - 1) { p.drawRect(lnX, y - r, iconPaneWidth, h + r); } else { p.drawRect(lnX, y - r, iconPaneWidth, h + 2 * r); } p.restore(); } if ((realLine >= 0) && (m_viewInternal->cache()->viewLine(z).startCol() == 0)) { QVector > startingRanges = m_view->textFolding().foldingRangesStartingOnLine(realLine); bool anyFolded = false; for (int i = 0; i < startingRanges.size(); ++i) if (startingRanges[i].second & Kate::TextFolding::Folded) { anyFolded = true; } Kate::TextLine tl = m_doc->kateTextLine(realLine); if (!startingRanges.isEmpty() || tl->markedAsFoldingStart()) { if (anyFolded) { paintTriangle(p, m_view->renderer()->config()->foldingColor(), lnX, y, iconPaneWidth, h, false); } else { paintTriangle(p, m_view->renderer()->config()->foldingColor(), lnX, y, iconPaneWidth, h, true); } } } lnX += iconPaneWidth; } // modified line system if (m_view->config()->lineModification() && realLine > -1 && !m_doc->url().isEmpty()) { // one pixel space ++lnX; Kate::TextLine tl = m_doc->plainKateTextLine(realLine); if (tl->markedAsModified()) { p.fillRect(lnX, y, 3, h, m_view->renderer()->config()->modifiedLineColor()); } if (tl->markedAsSavedOnDisk()) { p.fillRect(lnX, y, 3, h, m_view->renderer()->config()->savedLineColor()); } } } } KateIconBorder::BorderArea KateIconBorder::positionToArea(const QPoint &p) const { // see KateIconBorder::sizeHint() for pixel spacings int x = 0; if (m_iconBorderOn) { x += iconPaneWidth; if (p.x() <= x) { return IconBorder; } x += 2; } if (this->m_annotationBorderOn) { x += m_annotationBorderWidth; if (p.x() <= x) { return AnnotationBorder; } x += 2; } if (m_lineNumbersOn || m_dynWrapIndicators) { x += lineNumberWidth(); if (p.x() <= x) { return LineNumbers; } x += 2; } if (m_foldingMarkersOn) { x += iconPaneWidth; if (p.x() <= x) { return FoldingMarkers; } } if (m_view->config()->lineModification()) { x += 3 + 2; if (p.x() <= x) { return ModificationBorder; } } return None; } void KateIconBorder::mousePressEvent(QMouseEvent *e) { const KateTextLayout &t = m_viewInternal->yToKateTextLayout(e->y()); if (t.isValid()) { m_lastClickedLine = t.line(); if (positionToArea(e->pos()) != IconBorder && positionToArea(e->pos()) != AnnotationBorder) { QMouseEvent forward(QEvent::MouseButtonPress, QPoint(0, e->y()), e->button(), e->buttons(), e->modifiers()); m_viewInternal->mousePressEvent(&forward); } return e->accept(); } QWidget::mousePressEvent(e); } void KateIconBorder::showDelayedBlock(int line) { // save the line over which the mouse hovers // either we start the timer for delay, or we show the block immediately // if the moving range already exists m_nextHighlightBlock = line; if (!m_foldingRange) { if (!m_delayFoldingHlTimer.isActive()) { m_delayFoldingHlTimer.start(); } } else { showBlock(); } } void KateIconBorder::showBlock() { if (m_nextHighlightBlock == m_currentBlockLine) { return; } m_currentBlockLine = m_nextHighlightBlock; if (m_currentBlockLine >= m_doc->buffer().lines()) { return; } /** * compute to which folding range we belong * FIXME: optimize + perhaps have some better threshold or use timers! */ KTextEditor::Range newRange = KTextEditor::Range::invalid(); for (int line = m_currentBlockLine; line >= qMax(0, m_currentBlockLine - 1024); --line) { /** * try if we have folding range from that line, should be fast per call */ KTextEditor::Range foldingRange = m_doc->buffer().computeFoldingRangeForStartLine(line); if (!foldingRange.isValid()) { continue; } /** * does the range reach us? */ if (foldingRange.overlapsLine(m_currentBlockLine)) { newRange = foldingRange; break; } } if (newRange.isValid() && m_foldingRange && *m_foldingRange == newRange) { // new range equals the old one, nothing to do. return; } else { // the ranges differ, delete the old, if it exists delete m_foldingRange; m_foldingRange = nullptr; } if (newRange.isValid()) { //qCDebug(LOG_KTE) << "new folding hl-range:" << newRange; m_foldingRange = m_doc->newMovingRange(newRange, KTextEditor::MovingRange::ExpandRight); KTextEditor::Attribute::Ptr attr(new KTextEditor::Attribute()); /** * create highlighting color with alpha for the range! */ QColor result = m_view->renderer()->config()->foldingColor(); result.setAlphaF(0.5); attr->setBackground(QBrush(result)); m_foldingRange->setView(m_view); // use z depth defined in moving ranges interface m_foldingRange->setZDepth(-100.0); m_foldingRange->setAttribute(attr); } // show text preview, if a folded region starts here bool foldUnderMouse = false; if (m_foldingRange && m_view->config()->foldingPreview()) { const QPoint globalPos = QCursor::pos(); const QPoint pos = mapFromGlobal(globalPos); const KateTextLayout &t = m_view->m_viewInternal->yToKateTextLayout(pos.y()); if (t.isValid() && positionToArea(pos) == FoldingMarkers) { const int realLine = t.line(); foldUnderMouse = !m_view->textFolding().isLineVisible(realLine + 1); if (foldUnderMouse) { if (!m_foldingPreview) { m_foldingPreview = new KateTextPreview(m_view); m_foldingPreview->setAttribute(Qt::WA_ShowWithoutActivating); m_foldingPreview->setFrameStyle(QFrame::StyledPanel); // event filter to catch application WindowDeactivate event, to hide the preview window // qApp->installEventFilter(this); } // TODO: use KateViewInternal::maxLen() somehow to compute proper width for amount of lines to display // try using the end line of the range for proper popup height const int lineCount = qMin(m_foldingRange->numberOfLines() + 1, (height() - pos.y()) / m_view->renderer()->lineHeight()); m_foldingPreview->resize(m_view->width() / 2, lineCount * m_view->renderer()->lineHeight() + 2 * m_foldingPreview->frameWidth()); const int xGlobal = mapToGlobal(QPoint(width(), 0)).x(); const int yGlobal = m_view->mapToGlobal(m_view->cursorToCoordinate(KTextEditor::Cursor(realLine, 0))).y(); m_foldingPreview->move(QPoint(xGlobal, yGlobal) - m_foldingPreview->contentsRect().topLeft()); m_foldingPreview->setLine(realLine); m_foldingPreview->setCenterView(false); m_foldingPreview->setShowFoldedLines(true); m_foldingPreview->raise(); m_foldingPreview->show(); } } } if (!foldUnderMouse) { delete m_foldingPreview; } /** * repaint */ repaint(); } void KateIconBorder::hideBlock() { if (m_delayFoldingHlTimer.isActive()) { m_delayFoldingHlTimer.stop(); } m_nextHighlightBlock = -2; m_currentBlockLine = -1; delete m_foldingRange; m_foldingRange = nullptr; delete m_foldingPreview; } void KateIconBorder::leaveEvent(QEvent *event) { hideBlock(); removeAnnotationHovering(); QWidget::leaveEvent(event); } void KateIconBorder::mouseMoveEvent(QMouseEvent *e) { const KateTextLayout &t = m_viewInternal->yToKateTextLayout(e->y()); if (t.isValid()) { if (positionToArea(e->pos()) == FoldingMarkers) { showDelayedBlock(t.line()); } else { hideBlock(); } if (positionToArea(e->pos()) == AnnotationBorder) { KTextEditor::AnnotationModel *model = m_view->annotationModel() ? m_view->annotationModel() : m_doc->annotationModel(); if (model) { m_hoveredAnnotationGroupIdentifier = model->data( t.line(), (Qt::ItemDataRole) KTextEditor::AnnotationModel::GroupIdentifierRole ).toString(); showAnnotationTooltip(t.line(), e->globalPos()); QTimer::singleShot(0, this, SLOT(update())); } } else { if (positionToArea(e->pos()) == IconBorder) { m_doc->requestMarkTooltip(t.line(), e->globalPos()); } m_hoveredAnnotationGroupIdentifier.clear(); hideAnnotationTooltip(); QTimer::singleShot(0, this, SLOT(update())); } if (positionToArea(e->pos()) != IconBorder) { QPoint p = m_viewInternal->mapFromGlobal(e->globalPos()); QMouseEvent forward(QEvent::MouseMove, p, e->button(), e->buttons(), e->modifiers()); m_viewInternal->mouseMoveEvent(&forward); } } else { // remove hovering if it's still there removeAnnotationHovering(); } QWidget::mouseMoveEvent(e); } void KateIconBorder::mouseReleaseEvent(QMouseEvent *e) { const int cursorOnLine = m_viewInternal->yToKateTextLayout(e->y()).line(); if (cursorOnLine == m_lastClickedLine && cursorOnLine >= 0 && cursorOnLine <= m_doc->lastLine()) { BorderArea area = positionToArea(e->pos()); if (area == IconBorder) { if (e->button() == Qt::LeftButton) { if (!m_doc->handleMarkClick(cursorOnLine)) { KateViewConfig *config = m_view->config(); const uint editBits = m_doc->editableMarks(); // is the default or the only editable mark const uint singleMark = qPopulationCount(editBits) > 1 ? editBits & config->defaultMarkType() : editBits; if (singleMark) { if (m_doc->mark(cursorOnLine) & singleMark) { m_doc->removeMark(cursorOnLine, singleMark); } else { m_doc->addMark(cursorOnLine, singleMark); } } else if (config->allowMarkMenu()) { showMarkMenu(cursorOnLine, QCursor::pos()); } } } else if (e->button() == Qt::RightButton) { showMarkMenu(cursorOnLine, QCursor::pos()); } } if (area == FoldingMarkers) { // ask the folding info for this line, if any folds are around! QVector > startingRanges = m_view->textFolding().foldingRangesStartingOnLine(cursorOnLine); bool anyFolded = false; for (int i = 0; i < startingRanges.size(); ++i) if (startingRanges[i].second & Kate::TextFolding::Folded) { anyFolded = true; } // fold or unfold all ranges, remember if any action happened! bool actionDone = false; for (int i = 0; i < startingRanges.size(); ++i) { actionDone = (anyFolded ? m_view->textFolding().unfoldRange(startingRanges[i].first) : m_view->textFolding().foldRange(startingRanges[i].first)) || actionDone; } // if no action done, try to fold it, create non-persistent folded range, if possible! if (!actionDone) { // either use the fold for this line or the range that is highlighted ATM if any! KTextEditor::Range foldingRange = m_view->doc()->buffer().computeFoldingRangeForStartLine(cursorOnLine); if (!foldingRange.isValid() && m_foldingRange) { foldingRange = m_foldingRange->toRange(); } m_view->textFolding().newFoldingRange(foldingRange, Kate::TextFolding::Folded); } delete m_foldingPreview; } if (area == AnnotationBorder) { const bool singleClick = style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, this); if (e->button() == Qt::LeftButton && singleClick) { emit m_view->annotationActivated(m_view, cursorOnLine); } else if (e->button() == Qt::RightButton) { showAnnotationMenu(cursorOnLine, e->globalPos()); } } } QMouseEvent forward(QEvent::MouseButtonRelease, QPoint(0, e->y()), e->button(), e->buttons(), e->modifiers()); m_viewInternal->mouseReleaseEvent(&forward); } void KateIconBorder::mouseDoubleClickEvent(QMouseEvent *e) { int cursorOnLine = m_viewInternal->yToKateTextLayout(e->y()).line(); if (cursorOnLine == m_lastClickedLine && cursorOnLine <= m_doc->lastLine()) { BorderArea area = positionToArea(e->pos()); const bool singleClick = style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick, nullptr, this); if (area == AnnotationBorder && !singleClick) { emit m_view->annotationActivated(m_view, cursorOnLine); } } QMouseEvent forward(QEvent::MouseButtonDblClick, QPoint(0, e->y()), e->button(), e->buttons(), e->modifiers()); m_viewInternal->mouseDoubleClickEvent(&forward); } void KateIconBorder::wheelEvent(QWheelEvent *e) { QCoreApplication::sendEvent(m_viewInternal, e); } void KateIconBorder::showMarkMenu(uint line, const QPoint &pos) { if (m_doc->handleMarkContextMenu(line, pos)) { return; } if (!m_view->config()->allowMarkMenu()) { return; } QMenu markMenu; QMenu selectDefaultMark; auto selectDefaultMarkActionGroup = new QActionGroup(&selectDefaultMark); QVector vec(33); int i = 1; for (uint bit = 0; bit < 32; bit++) { MarkInterface::MarkTypes markType = (MarkInterface::MarkTypes)(1 << bit); if (!(m_doc->editableMarks() & markType)) { continue; } QAction *mA; QAction *dMA; const QPixmap icon = m_doc->markPixmap(markType); if (!m_doc->markDescription(markType).isEmpty()) { mA = markMenu.addAction(icon, m_doc->markDescription(markType)); dMA = selectDefaultMark.addAction(icon, m_doc->markDescription(markType)); } else { mA = markMenu.addAction(icon, i18n("Mark Type %1", bit + 1)); dMA = selectDefaultMark.addAction(icon, i18n("Mark Type %1", bit + 1)); } selectDefaultMarkActionGroup->addAction(dMA); mA->setData(i); mA->setCheckable(true); dMA->setData(i + 100); dMA->setCheckable(true); if (m_doc->mark(line) & markType) { mA->setChecked(true); } if (markType & KateViewConfig::global()->defaultMarkType()) { dMA->setChecked(true); } vec[i++] = markType; } if (markMenu.actions().count() == 0) { return; } if (markMenu.actions().count() > 1) { markMenu.addAction(i18n("Set Default Mark Type"))->setMenu(&selectDefaultMark); } QAction *rA = markMenu.exec(pos); if (!rA) { return; } int result = rA->data().toInt(); if (result > 100) { KateViewConfig::global()->setDefaultMarkType(vec[result - 100]); } else { MarkInterface::MarkTypes markType = (MarkInterface::MarkTypes) vec[result]; if (m_doc->mark(line) & markType) { m_doc->removeMark(line, markType); } else { m_doc->addMark(line, markType); } } } void KateIconBorder::showAnnotationTooltip(int line, const QPoint &pos) { KTextEditor::AnnotationModel *model = m_view->annotationModel() ? m_view->annotationModel() : m_doc->annotationModel(); if (model) { QVariant data = model->data(line, Qt::ToolTipRole); QString tip = data.toString(); if (!tip.isEmpty()) { QToolTip::showText(pos, data.toString(), this); } } } int KateIconBorder::annotationLineWidth(int line) { KTextEditor::AnnotationModel *model = m_view->annotationModel() ? m_view->annotationModel() : m_doc->annotationModel(); if (model) { QVariant data = model->data(line, Qt::DisplayRole); return data.toString().length() * m_maxCharWidth + 8; } return 8; } void KateIconBorder::updateAnnotationLine(int line) { if (annotationLineWidth(line) > m_annotationBorderWidth) { m_annotationBorderWidth = annotationLineWidth(line); updateGeometry(); QTimer::singleShot(0, this, SLOT(update())); } } void KateIconBorder::showAnnotationMenu(int line, const QPoint &pos) { QMenu menu; QAction a(i18n("Disable Annotation Bar"), &menu); a.setIcon(QIcon::fromTheme(QStringLiteral("dialog-close"))); menu.addAction(&a); emit m_view->annotationContextMenuAboutToShow(m_view, &menu, line); if (menu.exec(pos) == &a) { m_view->setAnnotationBorderVisible(false); } } void KateIconBorder::hideAnnotationTooltip() { QToolTip::hideText(); } void KateIconBorder::updateAnnotationBorderWidth() { m_annotationBorderWidth = 6; KTextEditor::AnnotationModel *model = m_view->annotationModel() ? m_view->annotationModel() : m_doc->annotationModel(); if (model) { for (int i = 0; i < m_view->doc()->lines(); i++) { int curwidth = annotationLineWidth(i); if (curwidth > m_annotationBorderWidth) { m_annotationBorderWidth = curwidth; } } } updateGeometry(); QTimer::singleShot(0, this, SLOT(update())); } void KateIconBorder::annotationModelChanged(KTextEditor::AnnotationModel *oldmodel, KTextEditor::AnnotationModel *newmodel) { if (oldmodel) { oldmodel->disconnect(this); } if (newmodel) { connect(newmodel, SIGNAL(reset()), this, SLOT(updateAnnotationBorderWidth())); connect(newmodel, SIGNAL(lineChanged(int)), this, SLOT(updateAnnotationLine(int))); } updateAnnotationBorderWidth(); } void KateIconBorder::displayRangeChanged() { hideBlock(); removeAnnotationHovering(); } //END KateIconBorder //BEGIN KateViewEncodingAction // Acording to http://www.iana.org/assignments/ianacharset-mib // the default/unknown mib value is 2. #define MIB_DEFAULT 2 bool lessThanAction(KSelectAction *a, KSelectAction *b) { return a->text() < b->text(); } void KateViewEncodingAction::Private::init() { QList actions; q->setToolBarMode(MenuMode); int i; foreach (const QStringList &encodingsForScript, KCharsets::charsets()->encodingsByScript()) { KSelectAction *tmp = new KSelectAction(encodingsForScript.at(0), q); for (i = 1; i < encodingsForScript.size(); ++i) { tmp->addAction(encodingsForScript.at(i)); } q->connect(tmp, SIGNAL(triggered(QAction*)), q, SLOT(_k_subActionTriggered(QAction*))); //tmp->setCheckable(true); actions << tmp; } qSort(actions.begin(), actions.end(), lessThanAction); foreach (KSelectAction *action, actions) { q->addAction(action); } } void KateViewEncodingAction::Private::_k_subActionTriggered(QAction *action) { if (currentSubAction == action) { return; } currentSubAction = action; bool ok = false; int mib = q->mibForName(action->text(), &ok); if (ok) { emit q->KSelectAction::triggered(action->text()); emit q->triggered(q->codecForMib(mib)); } } KateViewEncodingAction::KateViewEncodingAction(KTextEditor::DocumentPrivate *_doc, KTextEditor::ViewPrivate *_view, const QString &text, QObject *parent, bool saveAsMode) : KSelectAction(text, parent), doc(_doc), view(_view), d(new Private(this)) , m_saveAsMode(saveAsMode) { d->init(); connect(menu(), SIGNAL(aboutToShow()), this, SLOT(slotAboutToShow())); connect(this, SIGNAL(triggered(QString)), this, SLOT(setEncoding(QString))); } KateViewEncodingAction::~KateViewEncodingAction() { delete d; } void KateViewEncodingAction::slotAboutToShow() { setCurrentCodec(doc->config()->encoding()); } void KateViewEncodingAction::setEncoding(const QString &e) { /** * in save as mode => trigger saveAs */ if (m_saveAsMode) { doc->documentSaveAsWithEncoding(e); return; } /** * else switch encoding */ doc->userSetEncodingForNextReload(); doc->setEncoding(e); view->reloadFile(); } int KateViewEncodingAction::mibForName(const QString &codecName, bool *ok) const { // FIXME logic is good but code is ugly bool success = false; int mib = MIB_DEFAULT; KCharsets *charsets = KCharsets::charsets(); QTextCodec *codec = charsets->codecForName(codecName, success); if (!success) { // Maybe we got a description name instead codec = charsets->codecForName(charsets->encodingForName(codecName), success); } if (codec) { mib = codec->mibEnum(); } if (ok) { *ok = success; } if (success) { return mib; } qCWarning(LOG_KTE) << "Invalid codec name: " << codecName; return MIB_DEFAULT; } QTextCodec *KateViewEncodingAction::codecForMib(int mib) const { if (mib == MIB_DEFAULT) { // FIXME offer to change the default codec return QTextCodec::codecForLocale(); } else { return QTextCodec::codecForMib(mib); } } QTextCodec *KateViewEncodingAction::currentCodec() const { return codecForMib(currentCodecMib()); } bool KateViewEncodingAction::setCurrentCodec(QTextCodec *codec) { disconnect(this, SIGNAL(triggered(QString)), this, SLOT(setEncoding(QString))); int i, j; for (i = 0; i < actions().size(); ++i) { if (actions().at(i)->menu()) { for (j = 0; j < actions().at(i)->menu()->actions().size(); ++j) { if (!j && !actions().at(i)->menu()->actions().at(j)->data().isNull()) { continue; } if (actions().at(i)->menu()->actions().at(j)->isSeparator()) { continue; } if (codec == KCharsets::charsets()->codecForName(actions().at(i)->menu()->actions().at(j)->text())) { d->currentSubAction = actions().at(i)->menu()->actions().at(j); d->currentSubAction->setChecked(true); } else { actions().at(i)->menu()->actions().at(j)->setChecked(false); } } } } connect(this, SIGNAL(triggered(QString)), this, SLOT(setEncoding(QString))); return true; } QString KateViewEncodingAction::currentCodecName() const { return d->currentSubAction->text(); } bool KateViewEncodingAction::setCurrentCodec(const QString &codecName) { return setCurrentCodec(KCharsets::charsets()->codecForName(codecName)); } int KateViewEncodingAction::currentCodecMib() const { return mibForName(currentCodecName()); } bool KateViewEncodingAction::setCurrentCodec(int mib) { return setCurrentCodec(codecForMib(mib)); } //END KateViewEncodingAction //BEGIN KateViewBar related classes KateViewBarWidget::KateViewBarWidget(bool addCloseButton, QWidget *parent) : QWidget(parent) , m_viewBar(nullptr) { QHBoxLayout *layout = new QHBoxLayout(this); // NOTE: Here be cosmetics. layout->setMargin(0); // hide button if (addCloseButton) { QToolButton *hideButton = new QToolButton(this); hideButton->setAutoRaise(true); hideButton->setIcon(QIcon::fromTheme(QStringLiteral("dialog-close"))); connect(hideButton, SIGNAL(clicked()), SIGNAL(hideMe())); layout->addWidget(hideButton); layout->setAlignment(hideButton, Qt::AlignLeft | Qt::AlignTop); } // widget to be used as parent for the real content m_centralWidget = new QWidget(this); layout->addWidget(m_centralWidget); setLayout(layout); setFocusProxy(m_centralWidget); } KateViewBar::KateViewBar(bool external, QWidget *parent, KTextEditor::ViewPrivate *view) : QWidget(parent), m_external(external), m_view(view), m_permanentBarWidget(nullptr) { m_layout = new QVBoxLayout(this); m_stack = new QStackedWidget(this); m_layout->addWidget(m_stack); m_layout->setMargin(0); m_stack->hide(); hide(); } void KateViewBar::addBarWidget(KateViewBarWidget *newBarWidget) { // just ignore additional adds for already existing widgets if (hasBarWidget(newBarWidget)) { return; } // add new widget, invisible... newBarWidget->hide(); m_stack->addWidget(newBarWidget); newBarWidget->setAssociatedViewBar(this); connect(newBarWidget, SIGNAL(hideMe()), SLOT(hideCurrentBarWidget())); } void KateViewBar::removeBarWidget(KateViewBarWidget *barWidget) { // remove only if there if (!hasBarWidget(barWidget)) { return; } m_stack->removeWidget(barWidget); barWidget->setAssociatedViewBar(nullptr); barWidget->hide(); disconnect(barWidget, nullptr, this, nullptr); } void KateViewBar::addPermanentBarWidget(KateViewBarWidget *barWidget) { Q_ASSERT(barWidget); Q_ASSERT(!m_permanentBarWidget); m_stack->addWidget(barWidget); m_stack->setCurrentWidget(barWidget); m_stack->show(); m_permanentBarWidget = barWidget; m_permanentBarWidget->show(); setViewBarVisible(true); } void KateViewBar::removePermanentBarWidget(KateViewBarWidget *barWidget) { Q_ASSERT(m_permanentBarWidget == barWidget); Q_UNUSED(barWidget); const bool hideBar = m_stack->currentWidget() == m_permanentBarWidget; m_permanentBarWidget->hide(); m_stack->removeWidget(m_permanentBarWidget); m_permanentBarWidget = nullptr; if (hideBar) { m_stack->hide(); setViewBarVisible(false); } } bool KateViewBar::hasPermanentWidget(KateViewBarWidget *barWidget) const { return (m_permanentBarWidget == barWidget); } void KateViewBar::showBarWidget(KateViewBarWidget *barWidget) { Q_ASSERT(barWidget != nullptr); if (barWidget != qobject_cast(m_stack->currentWidget())) { hideCurrentBarWidget(); } // raise correct widget m_stack->setCurrentWidget(barWidget); barWidget->show(); barWidget->setFocus(Qt::ShortcutFocusReason); m_stack->show(); setViewBarVisible(true); } bool KateViewBar::hasBarWidget(KateViewBarWidget *barWidget) const { return m_stack->indexOf(barWidget) != -1; } void KateViewBar::hideCurrentBarWidget() { KateViewBarWidget *current = qobject_cast(m_stack->currentWidget()); if (current) { current->closed(); } // if we have any permanent widget, make it visible again if (m_permanentBarWidget) { m_stack->setCurrentWidget (m_permanentBarWidget); } else { // else: hide the bar m_stack->hide(); setViewBarVisible(false); } m_view->setFocus(); } void KateViewBar::setViewBarVisible(bool visible) { if (m_external) { if (visible) { m_view->mainWindow()->showViewBar(m_view); } else { m_view->mainWindow()->hideViewBar(m_view); } } else { setVisible(visible); } } bool KateViewBar::hiddenOrPermanent() const { KateViewBarWidget *current = qobject_cast(m_stack->currentWidget()); if (!isVisible() || (m_permanentBarWidget && m_permanentBarWidget == current)) { return true; } return false; } void KateViewBar::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Escape) { hideCurrentBarWidget(); return; } QWidget::keyPressEvent(event); } void KateViewBar::hideEvent(QHideEvent *event) { Q_UNUSED(event); // if (!event->spontaneous()) // m_view->setFocus(); } //END KateViewBar related classes KatePasteMenu::KatePasteMenu(const QString &text, KTextEditor::ViewPrivate *view) : KActionMenu(text, view) , m_view(view) { connect(menu(), SIGNAL(aboutToShow()), this, SLOT(slotAboutToShow())); } void KatePasteMenu::slotAboutToShow() { menu()->clear(); /** * insert complete paste history */ int i = 0; Q_FOREACH (const QString &text, KTextEditor::EditorPrivate::self()->clipboardHistory()) { /** * get text for the menu ;) */ QString leftPart = (text.size() > 48) ? (text.left(48) + QLatin1String("...")) : text; QAction *a = menu()->addAction(leftPart.replace(QLatin1String("\n"), QLatin1String(" ")), this, SLOT(paste())); a->setData(i++); } } void KatePasteMenu::paste() { if (!sender()) { return; } QAction *action = qobject_cast(sender()); if (!action) { return; } // get index int i = action->data().toInt(); if (i >= KTextEditor::EditorPrivate::self()->clipboardHistory().size()) { return; } // paste m_view->paste(&KTextEditor::EditorPrivate::self()->clipboardHistory()[i]); } diff --git a/src/view/kateviewhelpers.h b/src/view/kateviewhelpers.h index 9e2723ea..6d1e6786 100644 --- a/src/view/kateviewhelpers.h +++ b/src/view/kateviewhelpers.h @@ -1,634 +1,634 @@ /* This file is part of the KDE libraries Copyright (C) 2002 John Firebaugh Copyright (C) 2001 Anders Lund Copyright (C) 2001 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. */ #ifndef __KATE_VIEW_HELPERS_H__ #define __KATE_VIEW_HELPERS_H__ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "katetextline.h" namespace KTextEditor { class DocumentPrivate; } namespace KTextEditor { class ViewPrivate; } class KateViewInternal; #define MAXFOLDINGCOLORS 16 class KateLineInfo; class KateTextPreview; namespace KTextEditor { class Command; class AnnotationModel; class MovingRange; } class QTimer; class QVBoxLayout; /** * Class to layout KTextEditor::Message%s in KateView. Only the floating * positions TopInView, CenterInView, and BottomInView are supported. * AboveView and BelowView are not supported and ASSERT. */ class KateMessageLayout : public QLayout { public: explicit KateMessageLayout (QWidget *parent); - ~KateMessageLayout(); + ~KateMessageLayout() Q_DECL_OVERRIDE; void addWidget(QWidget *widget, KTextEditor::Message::MessagePosition pos); int count() const Q_DECL_OVERRIDE; QLayoutItem *itemAt(int index) const Q_DECL_OVERRIDE; void setGeometry(const QRect &rect) Q_DECL_OVERRIDE; QSize sizeHint() const Q_DECL_OVERRIDE; QLayoutItem *takeAt(int index) Q_DECL_OVERRIDE; void add(QLayoutItem *item, KTextEditor::Message::MessagePosition pos); private: void addItem(QLayoutItem *item) Q_DECL_OVERRIDE; // never called publically struct ItemWrapper { ItemWrapper(QLayoutItem *i, KTextEditor::Message::MessagePosition pos) : item(i) , position(pos) {} QLayoutItem * item = nullptr; KTextEditor::Message::MessagePosition position; }; QVector m_items; }; /** * This class is required because QScrollBar's sliderMoved() signal is * really supposed to be a sliderDragged() signal... so this way we can capture * MMB slider moves as well * * Also, it adds some useful indicators on the scrollbar. */ class KateScrollBar : public QScrollBar { Q_OBJECT public: KateScrollBar(Qt::Orientation orientation, class KateViewInternal *parent); - virtual ~KateScrollBar(); + ~KateScrollBar() Q_DECL_OVERRIDE; QSize sizeHint() const Q_DECL_OVERRIDE; inline bool showMarks() const { return m_showMarks; } inline void setShowMarks(bool b) { m_showMarks = b; update(); } inline bool showMiniMap() const { return m_showMiniMap; } void setShowMiniMap(bool b); inline bool miniMapAll() const { return m_miniMapAll; } inline void setMiniMapAll(bool b) { m_miniMapAll = b; updateGeometry(); update(); } inline bool miniMapWidth() const { return m_miniMapWidth; } inline void setMiniMapWidth(int width) { m_miniMapWidth = width; updateGeometry(); update(); } inline void queuePixmapUpdate() { m_updateTimer.start(); } Q_SIGNALS: void sliderMMBMoved(int value); protected: void mousePressEvent(QMouseEvent *e) Q_DECL_OVERRIDE; void mouseReleaseEvent(QMouseEvent *e) Q_DECL_OVERRIDE; void mouseMoveEvent(QMouseEvent *e) Q_DECL_OVERRIDE; void leaveEvent(QEvent *event) Q_DECL_OVERRIDE; bool eventFilter(QObject *object, QEvent *event) Q_DECL_OVERRIDE; void paintEvent(QPaintEvent *e) Q_DECL_OVERRIDE; void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE; void sliderChange(SliderChange change) Q_DECL_OVERRIDE; protected Q_SLOTS: void sliderMaybeMoved(int value); void marksChanged(); public Q_SLOTS: void updatePixmap(); private Q_SLOTS: void showTextPreview(); private: void showTextPreviewDelayed(); void hideTextPreview(); void redrawMarks(); void recomputeMarksPositions(); void miniMapPaintEvent(QPaintEvent *e); void normalPaintEvent(QPaintEvent *e); int minimapYToStdY(int y); const QColor charColor(const QVector &attributes, int &attributeIndex, const QList &decorations, const QColor &defaultColor, int x, QChar ch); bool m_middleMouseDown; bool m_leftMouseDown; KTextEditor::ViewPrivate *m_view; KTextEditor::DocumentPrivate *m_doc; class KateViewInternal *m_viewInternal; QPointer m_textPreview; QTimer m_delayTextPreviewTimer; QHash m_lines; bool m_showMarks; bool m_showMiniMap; bool m_miniMapAll; int m_miniMapWidth; QPixmap m_pixmap; int m_grooveHeight; QRect m_stdGroveRect; QRect m_mapGroveRect; QTimer m_updateTimer; QPoint m_toolTipPos; // lists of lines added/removed recently to avoid scrollbar flickering QHash m_linesAdded; int m_linesModified; static const unsigned char characterOpacity[256]; }; class KateIconBorder : public QWidget { Q_OBJECT public: KateIconBorder(KateViewInternal *internalView, QWidget *parent); - virtual ~KateIconBorder(); + ~KateIconBorder() Q_DECL_OVERRIDE; // VERY IMPORTANT ;) QSize sizeHint() const Q_DECL_OVERRIDE; void updateFont(); int lineNumberWidth() const; void setIconBorderOn(bool enable); void setLineNumbersOn(bool enable); void setRelLineNumbersOn(bool enable); void setAnnotationBorderOn(bool enable); void setDynWrapIndicators(int state); int dynWrapIndicators() const { return m_dynWrapIndicators; } bool dynWrapIndicatorsOn() const { return m_dynWrapIndicatorsOn; } void setFoldingMarkersOn(bool enable); void toggleIconBorder() { setIconBorderOn(!iconBorderOn()); } void toggleLineNumbers() { setLineNumbersOn(!lineNumbersOn()); } void toggleFoldingMarkers() { setFoldingMarkersOn(!foldingMarkersOn()); } inline bool iconBorderOn() const { return m_iconBorderOn; } inline bool lineNumbersOn() const { return m_lineNumbersOn; } inline bool viRelNumbersOn() const { return m_relLineNumbersOn; } inline bool foldingMarkersOn() const { return m_foldingMarkersOn; } inline bool annotationBorderOn() const { return m_annotationBorderOn; } void updateForCursorLineChange(); enum BorderArea { None, LineNumbers, IconBorder, FoldingMarkers, AnnotationBorder, ModificationBorder }; BorderArea positionToArea(const QPoint &) const; public Q_SLOTS: void updateAnnotationBorderWidth(); void updateAnnotationLine(int line); void annotationModelChanged(KTextEditor::AnnotationModel *oldmodel, KTextEditor::AnnotationModel *newmodel); void displayRangeChanged(); private: void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; void paintBorder(int x, int y, int width, int height); void mousePressEvent(QMouseEvent *) Q_DECL_OVERRIDE; void mouseMoveEvent(QMouseEvent *) Q_DECL_OVERRIDE; void mouseReleaseEvent(QMouseEvent *) Q_DECL_OVERRIDE; void mouseDoubleClickEvent(QMouseEvent *) Q_DECL_OVERRIDE; void leaveEvent(QEvent *event) Q_DECL_OVERRIDE; void wheelEvent(QWheelEvent *e) Q_DECL_OVERRIDE; void showMarkMenu(uint line, const QPoint &pos); void showAnnotationTooltip(int line, const QPoint &pos); void hideAnnotationTooltip(); void removeAnnotationHovering(); void showAnnotationMenu(int line, const QPoint &pos); int annotationLineWidth(int line); KTextEditor::ViewPrivate *m_view; KTextEditor::DocumentPrivate *m_doc; KateViewInternal *m_viewInternal; bool m_iconBorderOn: 1; bool m_lineNumbersOn: 1; bool m_relLineNumbersOn:1; bool m_updateRelLineNumbers:1; bool m_foldingMarkersOn: 1; bool m_dynWrapIndicatorsOn: 1; bool m_annotationBorderOn: 1; int m_dynWrapIndicators; int m_lastClickedLine; int m_cachedLNWidth; qreal m_maxCharWidth; int iconPaneWidth; int m_annotationBorderWidth; mutable QPixmap m_arrow; mutable QColor m_oldBackgroundColor; QPointer m_foldingPreview; KTextEditor::MovingRange *m_foldingRange; int m_nextHighlightBlock; int m_currentBlockLine; QTimer m_delayFoldingHlTimer; void showDelayedBlock(int line); void hideBlock(); private Q_SLOTS: void showBlock(); private: QString m_hoveredAnnotationGroupIdentifier; void initializeFoldingColors(); }; class KateViewEncodingAction: public KSelectAction { Q_OBJECT Q_PROPERTY(QString codecName READ currentCodecName WRITE setCurrentCodec) Q_PROPERTY(int codecMib READ currentCodecMib) public: KateViewEncodingAction(KTextEditor::DocumentPrivate *_doc, KTextEditor::ViewPrivate *_view, const QString &text, QObject *parent, bool saveAsMode = false); ~KateViewEncodingAction(); int mibForName(const QString &codecName, bool *ok = nullptr) const; QTextCodec *codecForMib(int mib) const; QTextCodec *currentCodec() const; bool setCurrentCodec(QTextCodec *codec); QString currentCodecName() const; bool setCurrentCodec(const QString &codecName); int currentCodecMib() const; bool setCurrentCodec(int mib); Q_SIGNALS: /** * Specific (proper) codec was selected */ void triggered(QTextCodec *codec); private: KTextEditor::DocumentPrivate *doc; KTextEditor::ViewPrivate *view; class Private { public: Private(KateViewEncodingAction *parent) : q(parent), currentSubAction(nullptr) { } void init(); void _k_subActionTriggered(QAction *); KateViewEncodingAction *q; QAction *currentSubAction; }; Private *const d; Q_PRIVATE_SLOT(d, void _k_subActionTriggered(QAction *)) const bool m_saveAsMode; private Q_SLOTS: void setEncoding(const QString &e); void slotAboutToShow(); }; class KateViewBar; class KateViewBarWidget : public QWidget { Q_OBJECT friend class KateViewBar; public: explicit KateViewBarWidget(bool addCloseButton, QWidget *parent = nullptr); virtual void closed() {} /// returns the currently associated KateViewBar and 0, if it is not associated KateViewBar *viewBar() { return m_viewBar; } protected: /** * @return widget that should be used to add controls to bar widget */ QWidget *centralWidget() { return m_centralWidget; } Q_SIGNALS: void hideMe(); // for friend class KateViewBar private: void setAssociatedViewBar(KateViewBar *bar) { m_viewBar = bar; } private: QWidget *m_centralWidget; KateViewBar *m_viewBar; // 0-pointer, if not added to a view bar }; class KateViewBar : public QWidget { Q_OBJECT public: KateViewBar(bool external, QWidget *parent, KTextEditor::ViewPrivate *view); /** * Adds a widget to this viewbar. * Widget is initially invisible, you should call showBarWidget, to show it. * Several widgets can be added to the bar, but only one can be visible */ void addBarWidget(KateViewBarWidget *newBarWidget); /** * Removes a widget from this viewbar. * Removing a widget makes sense if it takes a lot of space vertically, * because we use a QStackedWidget to maintain the same height for all * widgets in the viewbar. */ void removeBarWidget(KateViewBarWidget *barWidget); /** * @return if viewbar has widget @p barWidget */ bool hasBarWidget(KateViewBarWidget *barWidget) const; /** * Shows barWidget that was previously added with addBarWidget. * @see hideCurrentBarWidget */ void showBarWidget(KateViewBarWidget *barWidget); /** * Adds widget that will be always shown in the viewbar. * After adding permanent widget viewbar is immediately shown. * ViewBar with permanent widget won't hide itself * until permanent widget is removed. * OTOH showing/hiding regular barWidgets will work as usual * (they will be shown above permanent widget) * * If permanent widget already exists, asserts! */ void addPermanentBarWidget(KateViewBarWidget *barWidget); /** * Removes permanent bar widget from viewbar. * If no other viewbar widgets are shown, viewbar gets hidden. * * barWidget is not deleted, caller must do it if it wishes */ void removePermanentBarWidget(KateViewBarWidget *barWidget); /** * @return if viewbar has permanent widget @p barWidget */ bool hasPermanentWidget(KateViewBarWidget *barWidget) const; /** * @return true if the KateViewBar is hidden or displays a permanentBarWidget */ bool hiddenOrPermanent() const; public Q_SLOTS: /** * Hides currently shown bar widget */ void hideCurrentBarWidget(); protected: void keyPressEvent(QKeyEvent *event) Q_DECL_OVERRIDE; void hideEvent(QHideEvent *event) Q_DECL_OVERRIDE; private: /** * Shows or hides whole viewbar */ void setViewBarVisible(bool visible); bool m_external; private: KTextEditor::ViewPrivate *m_view; QStackedWidget *m_stack; KateViewBarWidget *m_permanentBarWidget; QVBoxLayout *m_layout; }; class KTEXTEDITOR_EXPORT KateCommandLineBar : public KateViewBarWidget { Q_OBJECT public: explicit KateCommandLineBar(KTextEditor::ViewPrivate *view, QWidget *parent = nullptr); ~KateCommandLineBar(); void setText(const QString &text, bool selected = true); void execute(const QString &text); public Q_SLOTS: void showHelpPage(); private: class KateCmdLineEdit *m_lineEdit; }; class KateCmdLineEdit : public KLineEdit { Q_OBJECT public: KateCmdLineEdit(KateCommandLineBar *bar, KTextEditor::ViewPrivate *view); bool event(QEvent *e) Q_DECL_OVERRIDE; void hideEvent(QHideEvent *e) Q_DECL_OVERRIDE; Q_SIGNALS: void hideRequested(); public Q_SLOTS: void slotReturnPressed(const QString &cmd); private Q_SLOTS: void hideLineEdit(); protected: void focusInEvent(QFocusEvent *ev) Q_DECL_OVERRIDE; void keyPressEvent(QKeyEvent *ev) Q_DECL_OVERRIDE; private: /** * Parse an expression denoting a position in the document. * Return the position as an integer. * Examples of expressions are "10" (the 10th line), * "$" (the last line), "." (the current line), * "'a" (the mark 'a), "/foo/" (a forwards search for "foo"), * and "?bar?" (a backwards search for "bar"). * @param string the expression to parse * @return the position, an integer */ int calculatePosition(QString string); void fromHistory(bool up); QString helptext(const QPoint &) const; KTextEditor::ViewPrivate *m_view; KateCommandLineBar *m_bar; bool m_msgMode; QString m_oldText; uint m_histpos; ///< position in the history uint m_cmdend; ///< the point where a command ends in the text, if we have a valid one. KTextEditor::Command *m_command; ///< For completing flags/args and interactiveness class KateCmdLnWhatsThis *m_help; QTimer *m_hideTimer; }; class KatePasteMenu : public KActionMenu { Q_OBJECT public: KatePasteMenu(const QString &text, KTextEditor::ViewPrivate *view); private: KTextEditor::ViewPrivate *m_view; private Q_SLOTS: void slotAboutToShow(); void paste(); }; #endif diff --git a/src/view/kateviewinternal.h b/src/view/kateviewinternal.h index 406cbc33..f8194c6c 100644 --- a/src/view/kateviewinternal.h +++ b/src/view/kateviewinternal.h @@ -1,470 +1,470 @@ /* This file is part of the KDE libraries Copyright (C) 2002-2007 Hamish Rodda Copyright (C) 2002 John Firebaugh Copyright (C) 2002 Joseph Wenninger Copyright (C) 2002 Christoph Cullmann Copyright (C) 2007 Mirko Stocker 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. */ #ifndef _KATE_VIEW_INTERNAL_ #define _KATE_VIEW_INTERNAL_ #include #include "katetextcursor.h" #include "katetextline.h" #include "katedocument.h" #include "kateview.h" #include "katerenderer.h" #include #include #include #include #include #include #include #include namespace KTextEditor { class MovingRange; class TextHintProvider; } class KateIconBorder; class KateScrollBar; class KateTextLayout; class KateTextAnimation; class KateAbstractInputMode; class ZoomEventFilter; class QScrollBar; class KateViewInternal : public QWidget { Q_OBJECT friend class KTextEditor::ViewPrivate; friend class KateIconBorder; friend class KateScrollBar; friend class CalculatingCursor; friend class BoundedCursor; friend class WrappingCursor; friend class KateAbstractInputMode; friend class ::KateTextPreview; public: enum Bias { left = -1, none = 0, right = 1 }; public: KateViewInternal(KTextEditor::ViewPrivate *view); - ~KateViewInternal(); + ~KateViewInternal() Q_DECL_OVERRIDE; KTextEditor::ViewPrivate *view() const { return m_view; } //BEGIN EDIT STUFF public: void editStart(); void editEnd(int editTagLineStart, int editTagLineEnd, bool tagFrom); void editSetCursor(const KTextEditor::Cursor &cursor); private: uint editSessionNumber; bool editIsRunning; KTextEditor::Cursor editOldCursor; KTextEditor::Range editOldSelection; //END //BEGIN TAG & CLEAR & UPDATE STUFF public: bool tagLine(const KTextEditor::Cursor &virtualCursor); bool tagLines(int start, int end, bool realLines = false); // cursors not const references as they are manipulated within bool tagLines(KTextEditor::Cursor start, KTextEditor::Cursor end, bool realCursors = false); bool tagRange(const KTextEditor::Range &range, bool realCursors); void tagAll(); void updateDirty(); void clear(); //END private Q_SLOTS: // Updates the view and requests a redraw. void updateView(bool changed = false, int viewLinesScrolled = 0); private: // Actually performs the updating, but doesn't call update(). void doUpdateView(bool changed = false, int viewLinesScrolled = 0); void makeVisible(const KTextEditor::Cursor &c, int endCol, bool force = false, bool center = false, bool calledExternally = false); public: // Start Position is a virtual cursor KTextEditor::Cursor startPos() const { return m_startPos; } int startLine() const { return m_startPos.line(); } int startX() const { return m_startX; } KTextEditor::Cursor endPos() const; int endLine() const; KateTextLayout yToKateTextLayout(int y) const; void prepareForDynWrapChange(); void dynWrapChanged(); public Q_SLOTS: void slotIncFontSizes(qreal step = 1.0); void slotDecFontSizes(qreal step = 1.0); void paintCursor(); private Q_SLOTS: void scrollLines(int line); // connected to the sliderMoved of the m_lineScroll void scrollViewLines(int offset); void scrollAction(int action); void scrollNextPage(); void scrollPrevPage(); void scrollPrevLine(); void scrollNextLine(); void scrollColumns(int x); // connected to the valueChanged of the m_columnScroll void viewSelectionChanged(); public: void doReturn(); void doSmartNewline(); void doDelete(); void doBackspace(); void doTabulator(); void doTranspose(); void doDeletePrevWord(); void doDeleteNextWord(); void cursorPrevChar(bool sel = false); void cursorNextChar(bool sel = false); void wordPrev(bool sel = false); void wordNext(bool sel = false); void home(bool sel = false); void end(bool sel = false); void cursorUp(bool sel = false); void cursorDown(bool sel = false); void cursorToMatchingBracket(bool sel = false); void scrollUp(); void scrollDown(); void topOfView(bool sel = false); void bottomOfView(bool sel = false); void pageUp(bool sel = false, bool half = false); void pageDown(bool sel = false, bool half = false); void top(bool sel = false); void bottom(bool sel = false); void top_home(bool sel = false); void bottom_end(bool sel = false); KTextEditor::Cursor getCursor() const { return m_cursor; } KTextEditor::Cursor getMouse() const { return m_mouse; } QPoint cursorToCoordinate(const KTextEditor::Cursor &cursor, bool realCursor = true, bool includeBorder = true) const; // by default, works on coordinates of the whole widget, eg. offsetted by the border KTextEditor::Cursor coordinatesToCursor(const QPoint &coord, bool includeBorder = true) const; QPoint cursorCoordinates(bool includeBorder = true) const; KTextEditor::Cursor findMatchingBracket(); KateIconBorder *iconBorder() const { return m_leftBorder; } // EVENT HANDLING STUFF - IMPORTANT private: void fixDropEvent(QDropEvent *event); protected: void hideEvent(QHideEvent *e) Q_DECL_OVERRIDE; void paintEvent(QPaintEvent *e) Q_DECL_OVERRIDE; bool eventFilter(QObject *obj, QEvent *e) Q_DECL_OVERRIDE; void keyPressEvent(QKeyEvent *) Q_DECL_OVERRIDE; void keyReleaseEvent(QKeyEvent *) Q_DECL_OVERRIDE; void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE; void mousePressEvent(QMouseEvent *) Q_DECL_OVERRIDE; void mouseDoubleClickEvent(QMouseEvent *) Q_DECL_OVERRIDE; void mouseReleaseEvent(QMouseEvent *) Q_DECL_OVERRIDE; void mouseMoveEvent(QMouseEvent *) Q_DECL_OVERRIDE; void leaveEvent(QEvent *) Q_DECL_OVERRIDE; void dragEnterEvent(QDragEnterEvent *) Q_DECL_OVERRIDE; void dragMoveEvent(QDragMoveEvent *) Q_DECL_OVERRIDE; void dropEvent(QDropEvent *) Q_DECL_OVERRIDE; void showEvent(QShowEvent *) Q_DECL_OVERRIDE; void wheelEvent(QWheelEvent *e) Q_DECL_OVERRIDE; void focusInEvent(QFocusEvent *) Q_DECL_OVERRIDE; void focusOutEvent(QFocusEvent *) Q_DECL_OVERRIDE; void inputMethodEvent(QInputMethodEvent *e) Q_DECL_OVERRIDE; void contextMenuEvent(QContextMenuEvent *e) Q_DECL_OVERRIDE; private Q_SLOTS: void tripleClickTimeout(); Q_SIGNALS: // emitted when KateViewInternal is not handling its own URI drops void dropEventPass(QDropEvent *); private Q_SLOTS: void slotRegionVisibilityChanged(); void slotRegionBeginEndAddedRemoved(unsigned int); private: void moveChar(Bias bias, bool sel); void moveEdge(Bias bias, bool sel); KTextEditor::Cursor maxStartPos(bool changed = false); void scrollPos(KTextEditor::Cursor &c, bool force = false, bool calledExternally = false, bool emitSignals = true); void scrollLines(int lines, bool sel); int linesDisplayed() const; int lineToY(int viewLine) const; void updateSelection(const KTextEditor::Cursor &, bool keepSel); void setSelection(const KTextEditor::Range &); void moveCursorToSelectionEdge(); void updateCursor(const KTextEditor::Cursor &newCursor, bool force = false, bool center = false, bool calledExternally = false); void updateBracketMarks(); void placeCursor(const QPoint &p, bool keepSelection = false, bool updateSelection = true); bool isTargetSelected(const QPoint &p); //Returns whether the given range affects the area currently visible in the view bool rangeAffectsView(const KTextEditor::Range &range, bool realCursors) const; void doDrag(); KateRenderer *renderer() const; KTextEditor::ViewPrivate *m_view; class KateIconBorder *m_leftBorder; int m_mouseX; int m_mouseY; int m_scrollX; int m_scrollY; ZoomEventFilter *m_zoomEventFilter; Qt::CursorShape m_mouseCursor; Kate::TextCursor m_cursor; KTextEditor::Cursor m_mouse; KTextEditor::Cursor m_displayCursor; bool m_possibleTripleClick; //Whether the current completion-item was expanded while the last press of ALT bool m_completionItemExpanded; QElapsedTimer m_altDownTime; // Bracket mark and corresponding decorative ranges KTextEditor::MovingRange *m_bm, *m_bmStart, *m_bmEnd; KTextEditor::MovingCursor *m_bmLastFlashPos; void updateBracketMarkAttributes(); enum DragState { diNone, diPending, diDragging }; struct _dragInfo { DragState state; QPoint start; QDrag *dragObject; } m_dragInfo; // // line scrollbar + first visible (virtual) line in the current view // KateScrollBar *m_lineScroll; QWidget *m_dummy; // These are now cursors to account for word-wrap. // Start Position is a virtual cursor Kate::TextCursor m_startPos; //Count of lines that are visible behind m_startPos. //This does not respect dynamic word wrap, so take it as an approximation. uint m_visibleLineCount; // This is set to false on resize or scroll (other than that called by makeVisible), // so that makeVisible is again called when a key is pressed and the cursor is in the same spot bool m_madeVisible; bool m_shiftKeyPressed; // How many lines to should be kept visible above/below the cursor when possible void setAutoCenterLines(int viewLines, bool updateView = true); int m_autoCenterLines; int m_minLinesVisible; // // column scrollbar + x position // QScrollBar *m_columnScroll; int m_startX; // has selection changed while your mouse or shift key is pressed bool m_selChangedByUser; KTextEditor::Cursor m_selectAnchor; enum SelectionMode { Default = 0, Mouse, Word, Line }; ///< for drag selection. uint m_selectionMode; // when drag selecting after double/triple click, keep the initial selected // word/line independent of direction. // They get set in the event of a double click, and is used with mouse move + leftbutton KTextEditor::Range m_selectionCached; // maximal length of textlines visible from given startLine int maxLen(int startLine); // are we allowed to scroll columns? bool columnScrollingPossible(); // the same for lines bool lineScrollingPossible(); // returns the maximum X value / col value a cursor can take for a specific line range int lineMaxCursorX(const KateTextLayout &line); int lineMaxCol(const KateTextLayout &line); class KateLayoutCache *cache() const; KateLayoutCache *m_layoutCache; // convenience methods KateTextLayout currentLayout() const; KateTextLayout previousLayout() const; KateTextLayout nextLayout() const; // find the cursor offset by (offset) view lines from a cursor. // when keepX is true, the column position will be calculated based on the x // position of the specified cursor. KTextEditor::Cursor viewLineOffset(const KTextEditor::Cursor &virtualCursor, int offset, bool keepX = false); KTextEditor::Cursor toRealCursor(const KTextEditor::Cursor &virtualCursor) const; KTextEditor::Cursor toVirtualCursor(const KTextEditor::Cursor &realCursor) const; // These variable holds the most recent maximum real & visible column number bool m_preserveX; int m_preservedX; int m_wrapChangeViewLine; KTextEditor::Cursor m_cachedMaxStartPos; // // implementation details for KTextEditor::FlashTextInterface // public: void flashChar(const KTextEditor::Cursor &pos, KTextEditor::Attribute::Ptr attribute); private: QPointer m_textAnimation; private Q_SLOTS: void doDragScroll(); void startDragScroll(); void stopDragScroll(); private: // Timers QTimer m_dragScrollTimer; QTimer m_scrollTimer; QTimer m_cursorTimer; QTimer m_textHintTimer; static const int s_scrollTime = 30; static const int s_scrollMargin = 16; private Q_SLOTS: void scrollTimeout(); void cursorTimeout(); void textHintTimeout(); void documentTextInserted(KTextEditor::Document *document, const KTextEditor::Range &range); void documentTextRemoved(KTextEditor::Document *document, const KTextEditor::Range &range, const QString &oldText); // // KTE::TextHintInterface // public: void registerTextHintProvider(KTextEditor::TextHintProvider *provider); void unregisterTextHintProvider(KTextEditor::TextHintProvider *provider); void setTextHintDelay(int delay); int textHintDelay() const; bool textHintsEnabled(); // not part of the interface private: QVector m_textHintProviders; int m_textHintDelay; QPoint m_textHintPos; // // IM input stuff // public: QVariant inputMethodQuery(Qt::InputMethodQuery query) const Q_DECL_OVERRIDE; private: KTextEditor::MovingRange *m_imPreeditRange; QList m_imPreeditRangeChildren; private: void mouseMoved(); void cursorMoved(); private: inline KTextEditor::DocumentPrivate *doc() { return m_view->doc(); } inline KTextEditor::DocumentPrivate *doc() const { return m_view->doc(); } // input modes private: QMap m_inputModes; KateAbstractInputMode *m_currentInputMode; }; #endif diff --git a/src/vimode/appcommands.h b/src/vimode/appcommands.h index 65ffb6c0..459c8b6c 100644 --- a/src/vimode/appcommands.h +++ b/src/vimode/appcommands.h @@ -1,119 +1,119 @@ /* This file is part of the KDE libraries Copyright (C) 2009 Erlend Hamberg Copyright (C) 2011 Svyatoslav Kuzmich 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 KATEVI_APP_COMMANDS_H #define KATEVI_APP_COMMANDS_H #include #include namespace KTextEditor { class MainWindow; } namespace KateVi { class AppCommands : public KTextEditor::Command { Q_OBJECT AppCommands(); static AppCommands* m_instance; public: - virtual ~AppCommands(); + ~AppCommands() Q_DECL_OVERRIDE; bool exec(KTextEditor::View *view, const QString &cmd, QString &msg, const KTextEditor::Range &range = KTextEditor::Range::invalid()) Q_DECL_OVERRIDE; bool help(KTextEditor::View *view, const QString &cmd, QString &msg) Q_DECL_OVERRIDE; static AppCommands* self() { if (m_instance == nullptr) { m_instance = new AppCommands(); } return m_instance; } private: /** * @returns a view in the given \p window that does not share a split * view with the given \p view. If such view could not be found, then * nullptr is returned. */ KTextEditor::View * findViewInDifferentSplitView(KTextEditor::MainWindow *window, KTextEditor::View *view); private Q_SLOTS: void closeCurrentDocument(); void closeCurrentView(); void closeCurrentSplitView(); void closeOtherSplitViews(); void quit(); private: QRegExp re_write; QRegExp re_close; QRegExp re_quit; QRegExp re_exit; QRegExp re_edit; QRegExp re_tabedit; QRegExp re_new; QRegExp re_split; QRegExp re_vsplit; QRegExp re_vclose; QRegExp re_only; }; class BufferCommands : public KTextEditor::Command { Q_OBJECT BufferCommands(); static BufferCommands* m_instance; public: - virtual ~BufferCommands(); + ~BufferCommands() Q_DECL_OVERRIDE; bool exec(KTextEditor::View *view, const QString &cmd, QString &msg, const KTextEditor::Range &range = KTextEditor::Range::invalid()) Q_DECL_OVERRIDE; bool help(KTextEditor::View *view, const QString &cmd, QString &msg) Q_DECL_OVERRIDE; static BufferCommands* self() { if (m_instance == nullptr) { m_instance = new BufferCommands(); } return m_instance; } private: void switchDocument(KTextEditor::View *, const QString &doc); void prevBuffer(KTextEditor::View *); void nextBuffer(KTextEditor::View *); void firstBuffer(KTextEditor::View *); void lastBuffer(KTextEditor::View *); void prevTab(KTextEditor::View *); void nextTab(KTextEditor::View *); void firstTab(KTextEditor::View *); void lastTab(KTextEditor::View *); void activateDocument(KTextEditor::View *, KTextEditor::Document *); QList documents(); }; } #endif /* KATEVI_APP_COMMANDS_H */ diff --git a/src/vimode/cmds.h b/src/vimode/cmds.h index c9d4e07c..5d6c423b 100644 --- a/src/vimode/cmds.h +++ b/src/vimode/cmds.h @@ -1,125 +1,125 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2003-2005 Anders Lund * Copyright (C) 2001-2010 Christoph Cullmann * Copyright (C) 2001 Charles Samuels * * 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 KATEVI_COMMANDS_H #define KATEVI_COMMANDS_H #include #include "kateregexpsearch.h" #include #include #include "mappings.h" #include namespace KTextEditor { class DocumentPrivate; } class KCompletion; namespace KateVi { /** * This KTextEditor::Command provides vi 'ex' commands */ class Commands : public KTextEditor::Command, public KateViCommandInterface { Commands() : KTextEditor::Command (QStringList() << mappingCommands() << QStringLiteral("d") << QStringLiteral("delete") << QStringLiteral("j") << QStringLiteral("c") << QStringLiteral("change") << QStringLiteral("<") << QStringLiteral(">") << QStringLiteral("y") << QStringLiteral("yank") << QStringLiteral("ma") << QStringLiteral("mark") << QStringLiteral("k")) { } static Commands *m_instance; public: - ~Commands() + ~Commands() Q_DECL_OVERRIDE { m_instance = nullptr; } /** * execute command on given range * @param view view to use for execution * @param cmd cmd string * @param msg message returned from running the command * @param range range to execute command on * @return success */ bool exec(class KTextEditor::View *view, const QString &cmd, QString &msg, const KTextEditor::Range &range = KTextEditor::Range(-1, -0, -1, 0)) Q_DECL_OVERRIDE; bool supportsRange(const QString &range) Q_DECL_OVERRIDE; /** This command does not have help. @see KTextEditor::Command::help */ bool help(class KTextEditor::View *, const QString &, QString &) Q_DECL_OVERRIDE { return false; } /** * Reimplement from KTextEditor::Command */ KCompletion *completionObject(KTextEditor::View *, const QString &) Q_DECL_OVERRIDE; static Commands *self() { if (m_instance == nullptr) { m_instance = new Commands(); } return m_instance; } private: const QStringList &mappingCommands(); Mappings::MappingMode modeForMapCommand(const QString &mapCommand); bool isMapCommandRecursive(const QString &mapCommand); }; /** * Support vim/sed style search and replace * @author Charles Samuels **/ class SedReplace : public KateCommands::SedReplace, public KateViCommandInterface { SedReplace() { } static SedReplace *m_instance; public: - ~SedReplace() + ~SedReplace() Q_DECL_OVERRIDE { m_instance = nullptr; } static SedReplace *self() { if (m_instance == nullptr) { m_instance = new SedReplace(); } return m_instance; } protected: bool interactiveSedReplace(KTextEditor::ViewPrivate *kateView, QSharedPointer interactiveSedReplace) Q_DECL_OVERRIDE; }; } #endif /* KATEVI_COMMANDS_H */ diff --git a/src/vimode/config/configtab.h b/src/vimode/config/configtab.h index fbcb461a..6e914203 100644 --- a/src/vimode/config/configtab.h +++ b/src/vimode/config/configtab.h @@ -1,67 +1,67 @@ /* This file is part of the KDE libraries and the Kate part. * * 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 KATEVI_CONFIG_TAB_H #define KATEVI_CONFIG_TAB_H #include #include class QTableWidget; namespace KateVi { namespace Ui { class ConfigWidget; } class ConfigTab : public KateConfigPage { Q_OBJECT public: explicit ConfigTab(QWidget *parent, Mappings *mappings); - virtual ~ConfigTab(); + ~ConfigTab() Q_DECL_OVERRIDE; QString name() const Q_DECL_OVERRIDE; protected: Ui::ConfigWidget *ui; private: void applyTab(QTableWidget *mappingsTable, Mappings::MappingMode mode); void reloadTab(QTableWidget *mappingsTable, Mappings::MappingMode mode); public Q_SLOTS: void apply() Q_DECL_OVERRIDE; void reload() Q_DECL_OVERRIDE; void reset() Q_DECL_OVERRIDE; void defaults() Q_DECL_OVERRIDE; private Q_SLOTS: void showWhatsThis(const QString &text); void addMappingRow(); void removeSelectedMappingRows(); void importNormalMappingRow(); private: Mappings *m_mappings; }; } #endif /* KATEVI_CONFIG_TAB_H */ diff --git a/src/vimode/emulatedcommandbar/emulatedcommandbar.h b/src/vimode/emulatedcommandbar/emulatedcommandbar.h index 6326b2b7..72bb6cce 100644 --- a/src/vimode/emulatedcommandbar/emulatedcommandbar.h +++ b/src/vimode/emulatedcommandbar/emulatedcommandbar.h @@ -1,131 +1,131 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2013-2016 Simon St James * * 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 KATEVI_EMULATED_COMMAND_BAR_H #define KATEVI_EMULATED_COMMAND_BAR_H #include "kateviewhelpers.h" #include #include #include #include "../searcher.h" #include "activemode.h" namespace KTextEditor { class ViewPrivate; class Command; } class QLabel; class QLayout; namespace KateVi { class MatchHighlighter; class InteractiveSedReplaceMode; class SearchMode; class CommandMode; /** * A KateViewBarWidget that attempts to emulate some of the features of Vim's own command bar, * including insertion of register contents via ctr-r; dismissal via * ctrl-c and ctrl-[; bi-directional incremental searching, with SmartCase; interactive sed-replace; * plus a few extensions such as completion from document and navigable sed search and sed replace history. */ class KTEXTEDITOR_EXPORT EmulatedCommandBar : public KateViewBarWidget { Q_OBJECT public: enum Mode { NoMode, SearchForward, SearchBackward, Command }; explicit EmulatedCommandBar(KateViInputMode* viInputMode, InputModeManager *viInputModeManager, QWidget *parent = nullptr); - virtual ~EmulatedCommandBar(); + ~EmulatedCommandBar() Q_DECL_OVERRIDE; void init(Mode mode, const QString &initialText = QString()); bool isActive(); void setCommandResponseMessageTimeout(long commandResponseMessageTimeOutMS); bool handleKeyPress(const QKeyEvent *keyEvent); bool isSendingSyntheticSearchCompletedKeypress(); void startInteractiveSearchAndReplace(QSharedPointer interactiveSedReplace); QString executeCommand(const QString &commandToExecute); void setViInputModeManager(InputModeManager *viInputModeManager); private: KateViInputMode *m_viInputMode; InputModeManager *m_viInputModeManager; bool m_isActive = false; bool m_wasAborted = true; Mode m_mode = NoMode; KTextEditor::ViewPrivate *m_view = nullptr; QLineEdit *m_edit = nullptr; QLabel *m_barTypeIndicator = nullptr; void showBarTypeIndicator(Mode mode); bool m_suspendEditEventFiltering = false; bool m_waitingForRegister = false ; QLabel *m_waitingForRegisterIndicator; bool m_insertedTextShouldBeEscapedForSearchingAsLiteral = false; void hideAllWidgetsExcept(QWidget* widgetToKeepVisible); friend class ActiveMode; QScopedPointer m_matchHighligher; QScopedPointer m_completer; QScopedPointer m_interactiveSedReplaceMode; QScopedPointer m_searchMode; QScopedPointer m_commandMode; void switchToMode(ActiveMode *newMode); ActiveMode *m_currentMode = nullptr; bool barHandledKeypress(const QKeyEvent* keyEvent); void insertRegisterContents(const QKeyEvent *keyEvent); bool eventFilter(QObject *object, QEvent *event) Q_DECL_OVERRIDE; void deleteSpacesToLeftOfCursor(); void deleteWordCharsToLeftOfCursor(); bool deleteNonWordCharsToLeftOfCursor(); void closed() Q_DECL_OVERRIDE; void closeWithStatusMessage(const QString& exitStatusMessage); QTimer *m_exitStatusMessageDisplayHideTimer; QLabel *m_exitStatusMessageDisplay; long m_exitStatusMessageHideTimeOutMS = 4000; void createAndAddBarTypeIndicator(QLayout* layout); void createAndAddEditWidget(QLayout* layout); void createAndAddExitStatusMessageDisplay(QLayout* layout); void createAndInitExitStatusMessageDisplayTimer(); void createAndAddWaitingForRegisterIndicator(QLayout* layout); private Q_SLOTS: void editTextChanged(const QString &newText); void startHideExitStatusMessageTimer(); }; } #endif /* KATEVI_EMULATED_COMMAND_BAR_H */ diff --git a/src/vimode/emulatedcommandbar/interactivesedreplacemode.h b/src/vimode/emulatedcommandbar/interactivesedreplacemode.h index 0948a581..81520109 100644 --- a/src/vimode/emulatedcommandbar/interactivesedreplacemode.h +++ b/src/vimode/emulatedcommandbar/interactivesedreplacemode.h @@ -1,62 +1,62 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2013-2016 Simon St James * * 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 KATEVI_EMULATED_COMMAND_BAR_INTERACTIVESEDREPLACEMODE_H #define KATEVI_EMULATED_COMMAND_BAR_INTERACTIVESEDREPLACEMODE_H #include "activemode.h" #include "../cmds.h" #include class QKeyEvent; class QLabel; namespace KateVi { class EmulatedCommandBar; class MatchHighlighter; class InteractiveSedReplaceMode : public ActiveMode { public: InteractiveSedReplaceMode(EmulatedCommandBar* emulatedCommandBar, MatchHighlighter* matchHighlighter, InputModeManager* viInputModeManager, KTextEditor::ViewPrivate* view); - virtual ~InteractiveSedReplaceMode() + ~InteractiveSedReplaceMode() Q_DECL_OVERRIDE { - }; + } void activate(QSharedPointer interactiveSedReplace); bool isActive() const { return m_isActive; } bool handleKeyPress(const QKeyEvent* keyEvent) Q_DECL_OVERRIDE; void deactivate(bool wasAborted) Q_DECL_OVERRIDE; QWidget *label(); private: void updateInteractiveSedReplaceLabelText(); void finishInteractiveSedReplace(); QSharedPointer m_interactiveSedReplacer; bool m_isActive; QLabel *m_interactiveSedReplaceLabel; }; } #endif diff --git a/src/vimode/emulatedcommandbar/searchmode.h b/src/vimode/emulatedcommandbar/searchmode.h index 3f31619c..cfa15225 100644 --- a/src/vimode/emulatedcommandbar/searchmode.h +++ b/src/vimode/emulatedcommandbar/searchmode.h @@ -1,71 +1,71 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2013-2016 Simon St James * * 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 KATEVI_EMULATED_COMMAND_BAR_SEARCHMODE_H #define KATEVI_EMULATED_COMMAND_BAR_SEARCHMODE_H #include "activemode.h" #include "../searcher.h" namespace KTextEditor { class ViewPrivate; } #include namespace KateVi { class EmulatedCommandBar; QString vimRegexToQtRegexPattern(const QString &vimRegexPattern); // TODO - move these generic helper functions into their own file? QString withCaseSensitivityMarkersStripped(const QString &originalSearchTerm); QString ensuredCharEscaped(const QString &originalString, QChar charToEscape); QStringList reversed(const QStringList &originalList); class SearchMode : public ActiveMode { public: SearchMode(EmulatedCommandBar* emulatedCommandBar, MatchHighlighter* matchHighlighter, InputModeManager* viInputModeManager, KTextEditor::ViewPrivate* view, QLineEdit* edit); - virtual ~SearchMode() + ~SearchMode() Q_DECL_OVERRIDE { - }; + } enum class SearchDirection { Forward, Backward }; void init(SearchDirection); bool handleKeyPress ( const QKeyEvent* keyEvent ) Q_DECL_OVERRIDE; void editTextChanged(const QString &newText) Q_DECL_OVERRIDE; CompletionStartParams completionInvoked(Completer::CompletionInvocation invocationType) Q_DECL_OVERRIDE; void completionChosen() Q_DECL_OVERRIDE; void deactivate(bool wasAborted) Q_DECL_OVERRIDE; bool isSendingSyntheticSearchCompletedKeypress() const { return m_isSendingSyntheticSearchCompletedKeypress; } private: QLineEdit *m_edit = nullptr; SearchDirection m_searchDirection; KTextEditor::Cursor m_startingCursorPos; KateVi::Searcher::SearchParams m_currentSearchParams; CompletionStartParams activateSearchHistoryCompletion(); enum BarBackgroundStatus { Normal, MatchFound, NoMatchFound }; void setBarBackground(BarBackgroundStatus status); bool m_isSendingSyntheticSearchCompletedKeypress = false; }; } #endif diff --git a/src/vimode/marks.cpp b/src/vimode/marks.cpp index 86d6c539..704e9780 100644 --- a/src/vimode/marks.cpp +++ b/src/vimode/marks.cpp @@ -1,309 +1,309 @@ /* This file is part of the KDE libraries * * 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. */ #include "marks.h" #include "kateview.h" #include "katedocument.h" #include #include #include using namespace KateVi; namespace { const QChar BeginEditYanked = QLatin1Char('['); const QChar EndEditYanked = QLatin1Char(']'); const QChar LastChange = QLatin1Char('.'); const QChar InsertStopped = QLatin1Char('^'); const QChar SelectionBegin = QLatin1Char('<'); const QChar SelectionEnd = QLatin1Char('>'); const QChar FirstUserMark = QLatin1Char('a'); const QChar LastUserMark = QLatin1Char('z'); const QChar BeforeJump = QLatin1Char('\''); const QChar BeforeJumpAlter = QLatin1Char('`'); const QChar UserMarks[] = {QLatin1Char('a'), QLatin1Char('b'), QLatin1Char('c'), QLatin1Char('d'), QLatin1Char('e'), QLatin1Char('f'), QLatin1Char('g'), QLatin1Char('h'), QLatin1Char('i'), QLatin1Char('j'), QLatin1Char('k'), QLatin1Char('l'), QLatin1Char('m'), QLatin1Char('n'), QLatin1Char('o'), QLatin1Char('p'), QLatin1Char('q'), QLatin1Char('r'), QLatin1Char('s'), QLatin1Char('t'), QLatin1Char('u'), QLatin1Char('v'), QLatin1Char('w'), QLatin1Char('x'), QLatin1Char('y'), QLatin1Char('z')}; } Marks::Marks(InputModeManager *imm) : m_inputModeManager(imm) , m_doc(imm->view()->doc()) , m_settingMark(false) { connect(m_doc, &KTextEditor::DocumentPrivate::markChanged, this, &Marks::markChanged); } Marks::~Marks() { } void Marks::readSessionConfig(const KConfigGroup &config) { QStringList marks = config.readEntry("ViMarks", QStringList()); for (int i = 0; i + 2 < marks.size(); i += 3) { KTextEditor::Cursor c(marks.at(i + 1).toInt(), marks.at(i + 2).toInt()); setMark(marks.at(i).at(0), c); } syncViMarksAndBookmarks(); } void Marks::writeSessionConfig(KConfigGroup &config) const { QStringList l; Q_FOREACH (QChar key, m_marks.keys()) { l << key << QString::number(m_marks.value(key)->line()) << QString::number(m_marks.value(key)->column()); } config.writeEntry("ViMarks", l); } void Marks::setMark(const QChar &_mark, const KTextEditor::Cursor &pos, const bool moveoninsert) { m_settingMark = true; uint marktype = m_doc->mark(pos.line()); // ` and ' is the same register (position before jump) const QChar mark = (_mark == BeforeJumpAlter) ? BeforeJump : _mark; // delete old cursor if any if (KTextEditor::MovingCursor *oldCursor = m_marks.value(mark)) { int number_of_marks = 0; foreach (QChar c, m_marks.keys()) { if (m_marks.value(c)->line() == oldCursor->line()) { number_of_marks++; } } if (number_of_marks == 1 && pos.line() != oldCursor->line()) { m_doc->removeMark(oldCursor->line(), KTextEditor::MarkInterface::markType01); } delete oldCursor; } KTextEditor::MovingCursor::InsertBehavior behavior = moveoninsert ? KTextEditor::MovingCursor::MoveOnInsert : KTextEditor::MovingCursor::StayOnInsert; // create and remember new one m_marks.insert(mark, m_doc->newMovingCursor(pos, behavior)); // Showing what mark we set: if (isShowable(mark)) { if (!(marktype & KTextEditor::MarkInterface::markType01)) { m_doc->addMark(pos.line(), KTextEditor::MarkInterface::markType01); } // only show message for active view if ( m_inputModeManager->view()->viewInputMode() == KTextEditor::View::ViInputMode ) { if (m_doc->activeView() == m_inputModeManager->view()) { m_inputModeManager->getViNormalMode()->message(i18n("Mark set: %1", mark)); } } } m_settingMark = false; } KTextEditor::Cursor Marks::getMarkPosition(const QChar &mark) const { if (m_marks.contains(mark)) { KTextEditor::MovingCursor *c = m_marks.value(mark); return KTextEditor::Cursor(c->line(), c->column()); } return KTextEditor::Cursor::invalid(); } void Marks::markChanged(KTextEditor::Document *doc, KTextEditor::Mark mark, KTextEditor::MarkInterface::MarkChangeAction action) { Q_UNUSED(doc) if (mark.type != KTextEditor::MarkInterface::Bookmark || m_settingMark) { return; } if (action == KTextEditor::MarkInterface::MarkRemoved) { foreach (QChar markerChar, m_marks.keys()) { if (m_marks.value(markerChar)->line() == mark.line) { m_marks.remove(markerChar); } } } else if (action == KTextEditor::MarkInterface::MarkAdded) { bool freeMarkerCharFound = false; - for (const QChar markerChar : UserMarks) { + for (const QChar &markerChar : UserMarks) { if (!m_marks.value(markerChar)) { setMark(markerChar, KTextEditor::Cursor(mark.line, 0)); freeMarkerCharFound = true; break; } } if (!freeMarkerCharFound) { m_inputModeManager->getViNormalMode()->error(i18n("There are no more chars for the next bookmark.")); } } } void Marks::syncViMarksAndBookmarks() { const QHash &marks = m_doc->marks(); // Each bookmark should have a vi mark on the same line. for (auto mark : marks) { if (!(mark->type & KTextEditor::MarkInterface::markType01)) { continue; } bool thereIsViMarkForThisLine = false; Q_FOREACH (const KTextEditor::MovingCursor *cursor, m_marks) { if (cursor->line() == mark->line) { thereIsViMarkForThisLine = true; break; } } if (thereIsViMarkForThisLine) { continue; } - for (const QChar markerChar : UserMarks) { + for (const QChar &markerChar : UserMarks) { if (!m_marks.value(markerChar)) { setMark(markerChar, KTextEditor::Cursor(mark->line, 0)); break; } } } // For showable vi mark a line should be bookmarked. Q_FOREACH (const QChar &markChar, m_marks.keys()) { if (!isShowable(markChar)) { continue; } bool thereIsKateMarkForThisLine = false; for (auto mark : marks) { if (!(mark->type & KTextEditor::MarkInterface::markType01)) { continue; } if (m_marks.value(markChar)->line() == mark->line) { thereIsKateMarkForThisLine = true; break; } } if (!thereIsKateMarkForThisLine) { m_doc->addMark(m_marks.value(markChar)->line(), KTextEditor::MarkInterface::markType01); } } } QString Marks::getMarksOnTheLine(int line) const { QString res; Q_FOREACH (QChar markerChar, m_marks.keys()) { if (m_marks.value(markerChar)->line() == line) { res += markerChar + QLatin1String(":") + QString::number(m_marks.value(markerChar)->column()) + QLatin1String(" "); } } return res; } bool Marks::isShowable(const QChar &mark) { return FirstUserMark <= mark && mark <= LastUserMark; } void Marks::setStartEditYanked(const KTextEditor::Cursor &pos) { setMark(BeginEditYanked, pos, false); } void Marks::setFinishEditYanked(const KTextEditor::Cursor &pos) { setMark(EndEditYanked, pos); } void Marks::setLastChange(const KTextEditor::Cursor &pos) { setMark(LastChange, pos); } void Marks::setInsertStopped(const KTextEditor::Cursor &pos) { setMark(InsertStopped, pos); } void Marks::setSelectionStart(const KTextEditor::Cursor &pos) { setMark(SelectionBegin, pos); } void Marks::setSelectionFinish(const KTextEditor::Cursor &pos) { setMark(SelectionEnd, pos); } void Marks::setUserMark(const QChar &mark, const KTextEditor::Cursor &pos) { Q_ASSERT(FirstUserMark <= mark && mark <= LastUserMark); setMark(mark, pos); } KTextEditor::Cursor Marks::getStartEditYanked() const { return getMarkPosition(BeginEditYanked); } KTextEditor::Cursor Marks::getFinishEditYanked() const { return getMarkPosition(EndEditYanked); } KTextEditor::Cursor Marks::getSelectionStart() const { return getMarkPosition(SelectionBegin); } KTextEditor::Cursor Marks::getSelectionFinish() const { return getMarkPosition(SelectionEnd); } KTextEditor::Cursor Marks::getLastChange() const { return getMarkPosition(LastChange); } KTextEditor::Cursor Marks::getInsertStopped() const { return getMarkPosition(InsertStopped); } diff --git a/src/vimode/modes/insertvimode.h b/src/vimode/modes/insertvimode.h index 3d1bba4e..e4850e96 100644 --- a/src/vimode/modes/insertvimode.h +++ b/src/vimode/modes/insertvimode.h @@ -1,120 +1,120 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2008-2011 Erlend Hamberg * Copyright (C) 2011 Svyatoslav Kuzmich * Copyright (C) 2012 - 2013 Simon St James * * 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 KATEVI_INSERT_VI_MODE_H #define KATEVI_INSERT_VI_MODE_H #include #include namespace KTextEditor { class ViewPrivate; } class KateViewInternal; class QKeyEvent; namespace KateVi { class Motion; /** * Commands for the vi insert mode */ enum BlockInsert { None, Prepend, Append, AppendEOL }; class KTEXTEDITOR_EXPORT InsertViMode : public ModeBase { Q_OBJECT public: explicit InsertViMode(InputModeManager *viInputModeManager, KTextEditor::ViewPrivate *view, KateViewInternal *viewInternal); - virtual ~InsertViMode(); + ~InsertViMode() Q_DECL_OVERRIDE; bool handleKeypress(const QKeyEvent *e) Q_DECL_OVERRIDE; bool commandInsertFromAbove(); bool commandInsertFromBelow(); bool commandDeleteWord(); bool commandDeleteLine(); bool commandNewLine(); bool commandDeleteCharBackward(); bool commandIndent(); bool commandUnindent(); bool commandToFirstCharacterInFile(); bool commandToLastCharacterInFile(); bool commandMoveOneWordLeft(); bool commandMoveOneWordRight(); bool commandCompleteNext(); bool commandCompletePrevious(); bool commandInsertContentOfRegister(); bool commandSwitchToNormalModeForJustOneCommand(); void setBlockPrependMode(Range blockRange); void setBlockAppendMode(Range blockRange, BlockInsert b); void setCount(int count) { m_count = count; - }; + } void setCountedRepeatsBeginOnNewLine(bool countedRepeatsBeginOnNewLine) { m_countedRepeatsBeginOnNewLine = countedRepeatsBeginOnNewLine; - }; + } protected: void leaveInsertMode(bool force = false); void completionFinished(); protected: BlockInsert m_blockInsert; unsigned int m_eolPos; // length of first line in eol mode before text is appended Range m_blockRange; QString m_keys; bool m_waitingRegister; unsigned int m_count; bool m_countedRepeatsBeginOnNewLine; bool m_isExecutingCompletion; QString m_textInsertedByCompletion; KTextEditor::Cursor m_textInsertedByCompletionEndPos; private Q_SLOTS: void textInserted(KTextEditor::Document *document, KTextEditor::Range range); }; } #endif /* KATEVI_INSERT_VI_MODE_H */ diff --git a/src/vimode/modes/modebase.h b/src/vimode/modes/modebase.h index 31462c05..5d2484e1 100644 --- a/src/vimode/modes/modebase.h +++ b/src/vimode/modes/modebase.h @@ -1,178 +1,178 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2008 - 2009 Erlend Hamberg * Copyright (C) 2009 Paul Gideon Dann * Copyright (C) 2011 Svyatoslav Kuzmich * Copyright (C) 2012 - 2013 Simon St James * * 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 KATEVI_MODE_BASE_H #define KATEVI_MODE_BASE_H #include #include #include "kateview.h" #include #include class QKeyEvent; class QString; class QRegExp; namespace KTextEditor { class DocumentPrivate; } namespace KateVi { class InputModeManager; enum Direction { Up, Down, Left, Right, Next, Prev }; class KTEXTEDITOR_EXPORT ModeBase : public QObject { Q_OBJECT public: ModeBase() : QObject() { } virtual ~ModeBase() {} /** * @return normal mode command accumulated so far */ QString getVerbatimKeys() const; virtual bool handleKeypress(const QKeyEvent *e) = 0; void setCount(unsigned int count) { m_count = count; } void setRegister(QChar reg) { m_register = reg; } void error(const QString &errorMsg); void message(const QString &msg); Range motionFindNext(); Range motionFindPrev(); virtual void goToPos(const Range &r); int getCount() const; protected: // helper methods void yankToClipBoard(QChar chosen_register, QString text); bool deleteRange(Range &r, OperationMode mode = LineWise, bool addToRegister = true); const QString getRange(Range &r, OperationMode mode = LineWise) const; const QString getLine(int line = -1) const; const QChar getCharUnderCursor() const; const QString getWordUnderCursor() const; const KTextEditor::Range getWordRangeUnderCursor() const; KTextEditor::Cursor findNextWordStart(int fromLine, int fromColumn, bool onlyCurrentLine = false) const; KTextEditor::Cursor findNextWORDStart(int fromLine, int fromColumn, bool onlyCurrentLine = false) const; KTextEditor::Cursor findPrevWordStart(int fromLine, int fromColumn, bool onlyCurrentLine = false) const; KTextEditor::Cursor findPrevWORDStart(int fromLine, int fromColumn, bool onlyCurrentLine = false) const; KTextEditor::Cursor findPrevWordEnd(int fromLine, int fromColumn, bool onlyCurrentLine = false) const; KTextEditor::Cursor findPrevWORDEnd(int fromLine, int fromColumn, bool onlyCurrentLine = false) const; KTextEditor::Cursor findWordEnd(int fromLine, int fromColumn, bool onlyCurrentLine = false) const; KTextEditor::Cursor findWORDEnd(int fromLine, int fromColumn, bool onlyCurrentLine = false) const; Range findSurroundingBrackets(const QChar &c1, const QChar &c2, bool inner, const QChar &nested1, const QChar &nested2) const; Range findSurrounding(const QRegExp &c1, const QRegExp &c2, bool inner = false) const; Range findSurroundingQuotes(const QChar &c, bool inner = false) const; int findLineStartingWitchChar(const QChar &c, int count, bool forward = true) const; void updateCursor(const KTextEditor::Cursor &c) const; const QChar getCharAtVirtualColumn(const QString &line, int virtualColumn, int tabWidht) const; void addToNumberUnderCursor(int count); Range goLineUp(); Range goLineDown(); Range goLineUpDown(int lines); Range goVisualLineUpDown(int lines); unsigned int linesDisplayed() const; void scrollViewLines(int l); bool isCounted() const { return m_iscounted; } bool startNormalMode(); bool startInsertMode(); bool startVisualMode(); bool startVisualLineMode(); bool startVisualBlockMode(); bool startReplaceMode(); QChar getChosenRegister(const QChar &defaultReg) const; QString getRegisterContent(const QChar ®); OperationMode getRegisterFlag(const QChar ®) const; void fillRegister(const QChar ®, const QString &text, OperationMode flag = CharWise); void switchView(Direction direction = Next); void addJump(KTextEditor::Cursor cursor); KTextEditor::Cursor getNextJump(KTextEditor::Cursor) const; KTextEditor::Cursor getPrevJump(KTextEditor::Cursor) const; inline KTextEditor::DocumentPrivate *doc() const { return m_view->doc(); - }; + } protected: QChar m_register; Range m_commandRange; unsigned int m_count = 0; int m_oneTimeCountOverride = -1; bool m_iscounted = false; QString m_extraWordCharacters; QString m_keysVerbatim; int m_stickyColumn = -1; bool m_lastMotionWasVisualLineUpOrDown; bool m_currentMotionWasVisualLineUpOrDown; KTextEditor::ViewPrivate *m_view; KateViewInternal *m_viewInternal; InputModeManager *m_viInputModeManager; // info message of vi mode QPointer m_infoMessage; }; } #endif diff --git a/src/vimode/modes/normalvimode.h b/src/vimode/modes/normalvimode.h index 5d78003d..1406af8d 100644 --- a/src/vimode/modes/normalvimode.h +++ b/src/vimode/modes/normalvimode.h @@ -1,398 +1,398 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2008 - 2009 Erlend Hamberg * Copyright (C) 2009 Paul Gideon Dann * Copyright (C) 2011 Svyatoslav Kuzmich * Copyright (C) 2012 - 2013 Simon St James * * 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 KATEVI_NORMAL_VI_MODE_H #define KATEVI_NORMAL_VI_MODE_H #include #include #include #include #include #include #include #include class QKeyEvent; class KateViInputMode; namespace KateVi { class Command; class Motion; class KeyParser; class InputModeManager; /** * Commands for the vi normal mode */ class KTEXTEDITOR_EXPORT NormalViMode : public ModeBase { Q_OBJECT friend KateViInputMode; public: explicit NormalViMode(InputModeManager *viInputModeManager, KTextEditor::ViewPrivate *view, KateViewInternal *viewInternal); - virtual ~NormalViMode(); + ~NormalViMode() Q_DECL_OVERRIDE; bool handleKeypress(const QKeyEvent *e) Q_DECL_OVERRIDE; bool commandEnterInsertMode(); bool commandEnterInsertModeAppend(); bool commandEnterInsertModeAppendEOL(); bool commandEnterInsertModeBeforeFirstNonBlankInLine(); bool commandEnterInsertModeLast(); bool commandEnterVisualMode(); bool commandEnterVisualLineMode(); bool commandEnterVisualBlockMode(); bool commandReselectVisual(); bool commandToOtherEnd(); bool commandEnterReplaceMode(); bool commandDelete(); bool commandDeleteToEOL(); bool commandDeleteLine(); bool commandMakeLowercase(); bool commandMakeLowercaseLine(); bool commandMakeUppercase(); bool commandMakeUppercaseLine(); bool commandChangeCase(); bool commandChangeCaseRange(); bool commandChangeCaseLine(); bool commandOpenNewLineUnder(); bool commandOpenNewLineOver(); bool commandJoinLines(); bool commandChange(); bool commandChangeLine(); bool commandChangeToEOL(); bool commandSubstituteChar(); bool commandSubstituteLine(); bool commandYank(); bool commandYankLine(); bool commandYankToEOL(); bool commandPaste(); bool commandPasteBefore(); bool commandgPaste(); bool commandgPasteBefore(); bool commandIndentedPaste(); bool commandIndentedPasteBefore(); bool commandDeleteChar(); bool commandDeleteCharBackward(); bool commandReplaceCharacter(); bool commandSwitchToCmdLine(); bool commandSearchBackward(); bool commandSearchForward(); bool commandUndo(); bool commandRedo(); bool commandSetMark(); bool commandIndentLine(); bool commandUnindentLine(); bool commandIndentLines(); bool commandUnindentLines(); bool commandScrollPageDown(); bool commandScrollPageUp(); bool commandScrollHalfPageUp(); bool commandScrollHalfPageDown(); bool commandCenterView(bool onFirst); bool commandCenterViewOnNonBlank(); bool commandCenterViewOnCursor(); bool commandTopView(bool onFirst); bool commandTopViewOnNonBlank(); bool commandTopViewOnCursor(); bool commandBottomView(bool onFirst); bool commandBottomViewOnNonBlank(); bool commandBottomViewOnCursor(); bool commandAbort(); bool commandPrintCharacterCode(); bool commandRepeatLastChange(); bool commandAlignLine(); bool commandAlignLines(); bool commandAddToNumber(); bool commandSubtractFromNumber(); bool commandPrependToBlock(); bool commandAppendToBlock(); bool commandGoToNextJump(); bool commandGoToPrevJump(); bool commandSwitchToLeftView(); bool commandSwitchToUpView(); bool commandSwitchToDownView(); bool commandSwitchToRightView(); bool commandSwitchToNextView(); bool commandSplitHoriz(); bool commandSplitVert(); bool commandCloseView(); bool commandSwitchToNextTab(); bool commandSwitchToPrevTab(); bool commandFormatLine(); bool commandFormatLines(); bool commandCollapseToplevelNodes(); bool commandCollapseLocal(); bool commandExpandAll(); bool commandExpandLocal(); bool commandToggleRegionVisibility(); bool commandStartRecordingMacro(); bool commandReplayMacro(); bool commandCloseWrite(); bool commandCloseNocheck(); // MOTIONS Range motionLeft(); Range motionRight(); Range motionDown(); Range motionUp(); Range motionPageDown(); Range motionPageUp(); Range motionHalfPageDown(); Range motionHalfPageUp(); Range motionUpToFirstNonBlank(); Range motionDownToFirstNonBlank(); Range motionWordForward(); Range motionWordBackward(); Range motionWORDForward(); Range motionWORDBackward(); Range motionToEndOfWord(); Range motionToEndOfWORD(); Range motionToEndOfPrevWord(); Range motionToEndOfPrevWORD(); Range motionFindChar(); Range motionFindCharBackward(); Range motionToChar(); Range motionToCharBackward(); Range motionRepeatlastTF(); Range motionRepeatlastTFBackward(); Range motionToEOL(); Range motionToColumn0(); Range motionToFirstCharacterOfLine(); Range motionToLineFirst(); Range motionToLineLast(); Range motionToScreenColumn(); Range motionToMark(); Range motionToMarkLine(); Range motionToMatchingItem(); Range motionToPreviousBraceBlockStart(); Range motionToNextBraceBlockStart(); Range motionToPreviousBraceBlockEnd(); Range motionToNextBraceBlockEnd(); Range motionToNextOccurrence(); Range motionToPrevOccurrence(); Range motionToFirstLineOfWindow(); Range motionToMiddleLineOfWindow(); Range motionToLastLineOfWindow(); Range motionToNextVisualLine(); Range motionToPrevVisualLine(); Range motionToPreviousSentence(); Range motionToNextSentence(); Range motionToBeforeParagraph(); Range motionToAfterParagraph(); Range motionToIncrementalSearchMatch(); // TEXT OBJECTS Range textObjectAWord(); Range textObjectInnerWord(); Range textObjectAWORD(); Range textObjectInnerWORD(); Range textObjectInnerSentence(); Range textObjectASentence(); Range textObjectInnerParagraph(); Range textObjectAParagraph(); Range textObjectAQuoteDouble(); Range textObjectInnerQuoteDouble(); Range textObjectAQuoteSingle(); Range textObjectInnerQuoteSingle(); Range textObjectABackQuote(); Range textObjectInnerBackQuote(); Range textObjectAParen(); Range textObjectInnerParen(); Range textObjectABracket(); Range textObjectInnerBracket(); Range textObjectACurlyBracket(); Range textObjectInnerCurlyBracket(); Range textObjectAInequalitySign(); Range textObjectInnerInequalitySign(); Range textObjectAComma(); Range textObjectInnerComma(); virtual void reset(); void beginMonitoringDocumentChanges(); protected: void resetParser(); void initializeCommands(); QRegExp generateMatchingItemRegex() const; void executeCommand(const Command *cmd); OperationMode getOperationMode() const; void highlightYank(const Range &range, const OperationMode mode = CharWise); void addHighlightYank(const KTextEditor::Range &range); bool motionWillBeUsedWithCommand() const { return !m_awaitingMotionOrTextObject.isEmpty(); }; void joinLines(unsigned int from, unsigned int to) const; void reformatLines(unsigned int from, unsigned int to) const; bool executeKateCommand(const QString &command); /** * Get the index of the first non-blank character from the given line. * * @param line The line to be picked. The current line will picked instead * if this parameter is set to a negative value. * @returns the index of the first non-blank character from the given line. * If a non-space character cannot be found, the 0 is returned. */ int getFirstNonBlank(int line = -1) const; Range textObjectComma(bool inner) const; void shrinkRangeAroundCursor(Range &toShrink, const Range &rangeToShrinkTo) const; KTextEditor::Cursor findSentenceStart(); KTextEditor::Cursor findSentenceEnd(); KTextEditor::Cursor findParagraphStart(); KTextEditor::Cursor findParagraphEnd(); protected: // The 'current position' is the current cursor position for non-linewise pastes, and the current // line for linewise. enum PasteLocation { AtCurrentPosition, AfterCurrentPosition }; bool paste(NormalViMode::PasteLocation pasteLocation, bool isgPaste, bool isIndentedPaste); KTextEditor::Cursor cursorPosAtEndOfPaste(const KTextEditor::Cursor &pasteLocation, const QString &pastedText) const; protected: QString m_keys; QString m_lastTFcommand; // holds the last t/T/f/F command so that it can be repeated with ;/, unsigned int m_countTemp; int m_motionOperatorIndex; int m_scroll_count_limit; QVector m_commands; QVector m_motions; QVector m_matchingCommands; QVector m_matchingMotions; QStack m_awaitingMotionOrTextObject; bool m_findWaitingForChar; bool m_isRepeatedTFcommand; bool m_linewiseCommand; bool m_commandWithMotion; bool m_lastMotionWasLinewiseInnerBlock; bool m_motionCanChangeWholeVisualModeSelection; bool m_commandShouldKeepSelection; bool m_deleteCommand; // Ctrl-c or ESC have been pressed, leading to a call to reset(). bool m_pendingResetIsDueToExit; bool m_isUndo; bool waitingForRegisterOrCharToSearch(); // item matching ('%' motion) QHash m_matchingItems; QRegExp m_matchItemRegex; KeyParser *m_keyParser; KTextEditor::Attribute::Ptr m_highlightYankAttribute; QSet m_highlightedYanks; QSet &highlightedYankForDocument(); KTextEditor::Cursor m_currentChangeEndMarker; KTextEditor::Cursor m_positionWhenIncrementalSearchBegan; private Q_SLOTS: void textInserted(KTextEditor::Document *document, KTextEditor::Range range); void textRemoved(KTextEditor::Document *, KTextEditor::Range); void undoBeginning(); void undoEnded(); void updateYankHighlightAttrib(); void clearYankHighlight(); void aboutToDeleteMovingInterfaceContent(); }; } #endif /* KATEVI_NORMAL_VI_MODE_H */ diff --git a/src/vimode/modes/replacevimode.h b/src/vimode/modes/replacevimode.h index a39a8b2a..b50aebd6 100644 --- a/src/vimode/modes/replacevimode.h +++ b/src/vimode/modes/replacevimode.h @@ -1,91 +1,91 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2008 Erlend Hamberg * * 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 KATEVI_REPLACE_MODE_H #define KATEVI_REPLACE_MODE_H #include namespace KateVi { /** * Commands for the vi replace mode */ class ReplaceViMode : public ModeBase { public: explicit ReplaceViMode(InputModeManager *viInputModeManager, KTextEditor::ViewPrivate *view, KateViewInternal *viewInternal); - virtual ~ReplaceViMode(); + ~ReplaceViMode() Q_DECL_OVERRIDE; /// Update the track of overwritten characters with the \p s character. inline void overwrittenChar(const QChar &s) { m_overwritten += s; } - void setCount(int count) { m_count = count;}; + void setCount(int count) { m_count = count;} protected: /** * Checks if the key is a valid command in replace mode. * * @returns true if a command was completed and executed, false otherwise. */ bool handleKeypress(const QKeyEvent *e) Q_DECL_OVERRIDE; private: /** * Replace the character at the current column with a character from * the same column but in a different line. * * @param offset The offset of the line to be picked. This value is * relative to the current line. * @returns true if the character could be replaced, false otherwise. */ bool commandInsertFromLine(int offset); // Auxiliar methods for moving the cursor in replace mode. bool commandMoveOneWordLeft(); bool commandMoveOneWordRight(); // Auxiliar methods for removing modifications. void backspace(); void commandBackWord(); void commandBackLine(); void leaveReplaceMode(); protected: unsigned int m_count; private: /// Keeps track of the characters that have been overwritten so far. QString m_overwritten; }; } #endif /* KATEVI_REPLACE_MODE_H */ diff --git a/src/vimode/modes/visualvimode.h b/src/vimode/modes/visualvimode.h index 70c866f3..6f72ec0d 100644 --- a/src/vimode/modes/visualvimode.h +++ b/src/vimode/modes/visualvimode.h @@ -1,121 +1,121 @@ /* This file is part of the KDE libraries and the Kate part. * * Copyright (C) 2008 - 2009 Erlend Hamberg * Copyright (C) 2009 Paul Gideon Dann * Copyright (C) 2011 Svyatoslav Kuzmich * Copyright (C) 2012 - 2013 Simon St James * * 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 KATEVI_VISUAL_VI_MODE_H #define KATEVI_VISUAL_VI_MODE_H #include #include namespace KateVi { class InputModeManager; class VisualViMode : public NormalViMode { Q_OBJECT public: explicit VisualViMode(InputModeManager *viInputModeManager, KTextEditor::ViewPrivate *view, KateViewInternal *viewInternal); - virtual ~VisualViMode(); + ~VisualViMode() Q_DECL_OVERRIDE; void init(); bool isVisualLine() const { return m_mode == VisualLineMode; } bool isVisualBlock() const { return m_mode == VisualBlockMode; } void switchStartEnd(); void reset() Q_DECL_OVERRIDE; void setVisualModeType(const ViMode mode); void saveRangeMarks(); void setStart(const KTextEditor::Cursor &c) { m_start = c; } KTextEditor::Cursor getStart() { return m_start; } void goToPos(const KTextEditor::Cursor &c); ViMode getLastVisualMode() const { return m_lastVisualMode; } const KTextEditor::Cursor &getStart() const { return m_start; } // Selects all lines in range; void selectLines(const KTextEditor::Range &range); // Selects range between c1 and c2, but includes the end cursor position. void selectInclusive(const KTextEditor::Cursor &c1, const KTextEditor::Cursor &c2); // Select block between c1 and c2. void selectBlockInclusive(const KTextEditor::Cursor &c1, const KTextEditor::Cursor &c2); protected: /** * Called when a motion/text object is used. Updates the cursor position * and modifies the range. A motion will only modify the end of the range * (i.e. move the cursor) while a text object may modify both the start and * the end. Overriden from the ModeBase class. */ void goToPos(const Range &r) Q_DECL_OVERRIDE; private: void initializeCommands(); public Q_SLOTS: /** * Updates the visual mode's range to reflect a new cursor position. This * needs to be called if modifying the range from outside the vi mode, e.g. * via mouse selection. */ void updateSelection(); private: KTextEditor::Cursor m_start; ViMode m_mode, m_lastVisualMode; }; } #endif /* KATEVI_VISUAL_VI_MODE_H */