diff --git a/src/mode/katemodemenu.h b/src/mode/katemodemenu.h index 2335305d..f8311c72 100644 --- a/src/mode/katemodemenu.h +++ b/src/mode/katemodemenu.h @@ -1,65 +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_MODEMENU_H__ #define KATE_MODEMENU_H__ #include #include #include #include "katedialogs.h" #include "katemodemanager.h" namespace KTextEditor { class DocumentPrivate; } class KateModeMenu : public KActionMenu { Q_OBJECT public: KateModeMenu(const QString &text, QObject *parent) : KActionMenu(text, parent) { init(); + setDelayed(false); } ~KateModeMenu(); void updateMenu(KTextEditor::Document *doc); private: void init(); QPointer m_doc; QStringList subMenusName; QStringList names; QList subMenus; QActionGroup *m_actionGroup; public Q_SLOTS: void slotAboutToShow(); private Q_SLOTS: void setType(QAction *); }; #endif diff --git a/src/schema/kateschema.h b/src/schema/kateschema.h index 4a08143d..dbd5de2e 100644 --- a/src/schema/kateschema.h +++ b/src/schema/kateschema.h @@ -1,110 +1,111 @@ /* This file is part of the KDE libraries Copyright (C) 2001-2003 Christoph Cullmann Copyright (C) 2002, 2003 Anders Lund * * 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_SCHEMA_H__ #define __KATE_SCHEMA_H__ #include #include #include #include #include namespace KTextEditor { class ViewPrivate; } class QActionGroup; class KateSchema { public: QString rawName; int shippedDefaultSchema; /** * construct translated name for shipped schemas */ QString translatedName() const { return shippedDefaultSchema ? i18nc("Color Schema", rawName.toUtf8().data()) : rawName; } }; class KateSchemaManager { public: KateSchemaManager(); /** * Config */ KConfig &config() { return m_config; } /** * return kconfiggroup for the given schema */ KConfigGroup schema(const QString &name); /** * return schema data for on schema */ KateSchema schemaData(const QString &name); /** * Constructs list of schemas atm known in config object */ QList list(); private: KConfig m_config; }; class KateViewSchemaAction : public KActionMenu { Q_OBJECT public: KateViewSchemaAction(const QString &text, QObject *parent) : KActionMenu(text, parent) { init(); + setDelayed(false); } void updateMenu(KTextEditor::ViewPrivate *view); private: void init(); QPointer m_view; QStringList names; QActionGroup *m_group; int last; public Q_SLOTS: void slotAboutToShow(); private Q_SLOTS: void setSchema(); }; #endif diff --git a/src/script/katescriptaction.cpp b/src/script/katescriptaction.cpp index 1eae2950..63b65052 100644 --- a/src/script/katescriptaction.cpp +++ b/src/script/katescriptaction.cpp @@ -1,164 +1,165 @@ /* This file is part of the KDE libraries Copyright (C) 2010 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. */ #include "katescriptaction.h" #include "katecommandlinescript.h" #include "katescriptmanager.h" #include "kateview.h" #include "katedocument.h" #include "kateglobal.h" #include "kateviewhelpers.h" #include "katepartdebug.h" #include "katecmd.h" #include "kateabstractinputmode.h" #include #include #include #include //BEGIN KateScriptAction KateScriptAction::KateScriptAction(const QString &cmd, const QJsonObject &action, KTextEditor::ViewPrivate *view) : QAction(i18nc("Script command name", action.value(QStringLiteral("name")).toString().toUtf8().data()), view) , m_view(view) , m_command(cmd) , m_interactive(action.value(QStringLiteral("interactive")).toBool()) { const QString icon = action.value(QStringLiteral("icon")).toString(); if (!icon.isEmpty()) { setIcon(QIcon::fromTheme(icon)); } connect(this, SIGNAL(triggered(bool)), this, SLOT(exec())); } KateScriptAction::~KateScriptAction() { } void KateScriptAction::exec() { if (m_interactive) { m_view->currentInputMode()->launchInteractiveCommand(m_command + QLatin1Char(' ')); } else { KTextEditor::Command *p = KateCmd::self()->queryCommand(m_command); if (p) { QString msg; p->exec(m_view, m_command, msg); } } } //END KateScriptAction //BEGIN KateScriptActionMenu KateScriptActionMenu::KateScriptActionMenu(KTextEditor::ViewPrivate *view, const QString &text) : KActionMenu(QIcon::fromTheme(QStringLiteral("code-context")), text, view) , m_view(view) { repopulate(); + setDelayed(false); // on script-reload signal, repopulate script menu connect(KTextEditor::EditorPrivate::self()->scriptManager(), SIGNAL(reloaded()), this, SLOT(repopulate())); } KateScriptActionMenu::~KateScriptActionMenu() { cleanup(); } void KateScriptActionMenu::cleanup() { // delete menus and actions for real qDeleteAll(m_menus); m_menus.clear(); qDeleteAll(m_actions); m_actions.clear(); } void KateScriptActionMenu::repopulate() { // if the view is already hooked into the GUI, first remove it // now and add it later, so that the changes we do here take effect KXMLGUIFactory *viewFactory = m_view->factory(); if (viewFactory) { viewFactory->removeClient(m_view); } // remove existing menu actions cleanup(); // now add all command line script commands QVector scripts = KTextEditor::EditorPrivate::self()->scriptManager()->commandLineScripts(); QHash menus; foreach (KateCommandLineScript *script, scripts) { /** * traverse actions */ const QJsonArray &actions = script->commandHeader().actions(); Q_FOREACH (const QJsonValue value, actions) { /** * action is a value */ const QJsonObject action = value.toObject(); /** * get command */ const QString cmd = action.value(QStringLiteral("function")).toString(); // show in a category submenu? QMenu *m = menu(); QString category = action.value(QStringLiteral("category")).toString(); if (!category.isEmpty()) { category = i18nc("Script command category", category.toUtf8().data()); m = menus[category]; if (!m) { m = menu()->addMenu(category); menus.insert(category, m); m_menus.append(m); } } // create action + add to menu QAction *a = new KateScriptAction(cmd, action, m_view); m->addAction(a); m_view->actionCollection()->addAction(QLatin1String("tools_scripts_") + cmd, a); const QString shortcut = action.value(QStringLiteral("shortcut")).toString(); if (!shortcut.isEmpty()) { m_view->actionCollection()->setDefaultShortcut(a, shortcut); } m_actions.append(a); } } // finally add the view to the xml factory again, if it initially was there if (viewFactory) { viewFactory->addClient(m_view); } } //END KateScriptActionMenu diff --git a/src/syntax/katehighlightmenu.h b/src/syntax/katehighlightmenu.h index 064419d4..a52c14af 100644 --- a/src/syntax/katehighlightmenu.h +++ b/src/syntax/katehighlightmenu.h @@ -1,64 +1,65 @@ /* This file is part of the KDE libraries Copyright (C) 2001-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_HIGHLIGHTMENU_H__ #define KATE_HIGHLIGHTMENU_H__ #include #include #include #include namespace KTextEditor { class DocumentPrivate; } class KateHighlightingMenu : public KActionMenu { Q_OBJECT public: KateHighlightingMenu(const QString &text, QObject *parent) : KActionMenu(text, parent) { init(); + setDelayed(false); } ~KateHighlightingMenu(); void updateMenu(KTextEditor::DocumentPrivate *doc); private: void init(); QPointer m_doc; QStringList subMenusName; QStringList names; QList subMenus; QList subActions; QActionGroup *m_actionGroup; public Q_SLOTS: void slotAboutToShow(); private Q_SLOTS: void setHl(); }; #endif diff --git a/src/utils/kateautoindent.cpp b/src/utils/kateautoindent.cpp index 344f01d4..78a0209e 100644 --- a/src/utils/kateautoindent.cpp +++ b/src/utils/kateautoindent.cpp @@ -1,498 +1,499 @@ /* This file is part of the KDE libraries Copyright (C) 2003 Jesse Yurkovich Copyright (C) 2004 >Anders Lund (KateVarIndent class) Copyright (C) 2005 Dominik Haumann (basic support for config page) 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 "kateautoindent.h" #include "kateconfig.h" #include "katehighlight.h" #include "kateglobal.h" #include "kateindentscript.h" #include "katescriptmanager.h" #include "kateview.h" #include "kateextendedattribute.h" #include "katedocument.h" #include "katepartdebug.h" #include #include namespace { inline const QString MODE_NONE() { return QStringLiteral("none"); } inline const QString MODE_NORMAL() { return QStringLiteral("normal"); } } //BEGIN KateAutoIndent QStringList KateAutoIndent::listModes() { QStringList l; for (int i = 0; i < modeCount(); ++i) { l << modeDescription(i); } return l; } QStringList KateAutoIndent::listIdentifiers() { QStringList l; for (int i = 0; i < modeCount(); ++i) { l << modeName(i); } return l; } int KateAutoIndent::modeCount() { // inbuild modes + scripts return 2 + KTextEditor::EditorPrivate::self()->scriptManager()->indentationScriptCount(); } QString KateAutoIndent::modeName(int mode) { if (mode == 0 || mode >= modeCount()) { return MODE_NONE(); } if (mode == 1) { return MODE_NORMAL(); } return KTextEditor::EditorPrivate::self()->scriptManager()->indentationScriptByIndex(mode - 2)->indentHeader().baseName(); } QString KateAutoIndent::modeDescription(int mode) { if (mode == 0 || mode >= modeCount()) { return i18nc("Autoindent mode", "None"); } if (mode == 1) { return i18nc("Autoindent mode", "Normal"); } const QString &name = KTextEditor::EditorPrivate::self()->scriptManager()->indentationScriptByIndex(mode - 2)->indentHeader().name(); return i18nc("Autoindent mode", name.toUtf8().constData()); } QString KateAutoIndent::modeRequiredStyle(int mode) { if (mode == 0 || mode == 1 || mode >= modeCount()) { return QString(); } return KTextEditor::EditorPrivate::self()->scriptManager()->indentationScriptByIndex(mode - 2)->indentHeader().requiredStyle(); } uint KateAutoIndent::modeNumber(const QString &name) { for (int i = 0; i < modeCount(); ++i) if (modeName(i) == name) { return i; } return 0; } KateAutoIndent::KateAutoIndent(KTextEditor::DocumentPrivate *_doc) : QObject(_doc), doc(_doc), m_script(nullptr) { // don't call updateConfig() here, document might is not ready for that.... // on script reload, the script pointer is invalid -> force reload connect(KTextEditor::EditorPrivate::self()->scriptManager(), SIGNAL(reloaded()), this, SLOT(reloadScript())); } KateAutoIndent::~KateAutoIndent() { } QString KateAutoIndent::tabString(int length, int align) const { QString s; length = qMin(length, 256); // sanity check for large values of pos int spaces = qBound(0, align - length, 256); if (!useSpaces) { s.append(QString(length / tabWidth, QLatin1Char('\t'))); length = length % tabWidth; } s.append(QString(length + spaces, QLatin1Char(' '))); return s; } bool KateAutoIndent::doIndent(int line, int indentDepth, int align) { Kate::TextLine textline = doc->plainKateTextLine(line); // textline not found, cu if (!textline) { return false; } // sanity check if (indentDepth < 0) { indentDepth = 0; } const QString oldIndentation = textline->leadingWhitespace(); // Preserve existing "tabs then spaces" alignment if and only if: // - no alignment was passed to doIndent and // - we aren't using spaces for indentation and // - we aren't rounding indentation up to the next multiple of the indentation width and // - we aren't using a combination to tabs and spaces for alignment, or in other words // the indent width is a multiple of the tab width. bool preserveAlignment = !useSpaces && keepExtra && indentWidth % tabWidth == 0; if (align == 0 && preserveAlignment) { // Count the number of consecutive spaces at the end of the existing indentation int i = oldIndentation.size() - 1; while (i >= 0 && oldIndentation.at(i) == QLatin1Char(' ')) { --i; } // Use the passed indentDepth as the alignment, and set the indentDepth to // that value minus the number of spaces found (but don't let it get negative). align = indentDepth; indentDepth = qMax(0, align - (oldIndentation.size() - 1 - i)); } QString indentString = tabString(indentDepth, align); // Modify the document *ONLY* if smth has really changed! if (oldIndentation != indentString) { // remove leading whitespace, then insert the leading indentation doc->editStart(); doc->editRemoveText(line, 0, oldIndentation.length()); doc->editInsertText(line, 0, indentString); doc->editEnd(); } return true; } bool KateAutoIndent::doIndentRelative(int line, int change) { Kate::TextLine textline = doc->plainKateTextLine(line); // get indent width of current line int indentDepth = textline->indentDepth(tabWidth); int extraSpaces = indentDepth % indentWidth; // add change indentDepth += change; // if keepExtra is off, snap to a multiple of the indentWidth if (!keepExtra && extraSpaces > 0) { if (change < 0) { indentDepth += indentWidth - extraSpaces; } else { indentDepth -= extraSpaces; } } // do indent return doIndent(line, indentDepth); } void KateAutoIndent::keepIndent(int line) { // no line in front, no work... if (line <= 0) { return; } // keep indentation: find line with content int nonEmptyLine = line - 1; while (nonEmptyLine >= 0) { if (doc->lineLength(nonEmptyLine) > 0) { break; } --nonEmptyLine; } Kate::TextLine prevTextLine = doc->plainKateTextLine(nonEmptyLine); Kate::TextLine textLine = doc->plainKateTextLine(line); // textline not found, cu if (!prevTextLine || !textLine) { return; } const QString previousWhitespace = prevTextLine->leadingWhitespace(); // remove leading whitespace, then insert the leading indentation doc->editStart(); if (!keepExtra) { const QString currentWhitespace = textLine->leadingWhitespace(); doc->editRemoveText(line, 0, currentWhitespace.length()); } doc->editInsertText(line, 0, previousWhitespace); doc->editEnd(); } void KateAutoIndent::reloadScript() { // small trick to force reload m_script = nullptr; // prevent dangling pointer QString currentMode = m_mode; m_mode = QString(); setMode(currentMode); } void KateAutoIndent::scriptIndent(KTextEditor::ViewPrivate *view, const KTextEditor::Cursor &position, QChar typedChar) { // start edit doc->pushEditState(); doc->editStart(); QPair result = m_script->indent(view, position, typedChar, indentWidth); int newIndentInChars = result.first; // handle negative values special if (newIndentInChars < -1) { // do nothing atm } // reuse indentation of the previous line, just like the "normal" indenter else if (newIndentInChars == -1) { // keep indent of previous line keepIndent(position.line()); } // get align else { // we got a positive or zero indent to use... doIndent(position.line(), newIndentInChars, result.second); } // end edit in all cases doc->editEnd(); doc->popEditState(); } bool KateAutoIndent::isStyleProvided(const KateIndentScript *script, const KateHighlighting *highlight) { QString requiredStyle = script->indentHeader().requiredStyle(); return (requiredStyle.isEmpty() || requiredStyle == highlight->style()); } void KateAutoIndent::setMode(const QString &name) { // bail out, already set correct mode... if (m_mode == name) { return; } // cleanup m_script = nullptr; // first, catch easy stuff... normal mode and none, easy... if (name.isEmpty() || name == MODE_NONE()) { m_mode = MODE_NONE(); return; } if (name == MODE_NORMAL()) { m_mode = MODE_NORMAL(); return; } // handle script indenters, if any for this name... KateIndentScript *script = KTextEditor::EditorPrivate::self()->scriptManager()->indentationScript(name); if (script) { if (isStyleProvided(script, doc->highlight())) { m_script = script; m_mode = name; return; } else { qCWarning(LOG_KTE) << "mode" << name << "requires a different highlight style: highlighting '" << doc->highlight()->name() << "' (" << doc->highlight()->version() << "), style '" << doc->highlight()->style() << "'" ", but script require '" << script->indentHeader().requiredStyle() << "'" ; } } else { qCWarning(LOG_KTE) << "mode" << name << "does not exist"; } // Fall back to normal m_mode = MODE_NORMAL(); } void KateAutoIndent::checkRequiredStyle() { if (m_script) { if (!isStyleProvided(m_script, doc->highlight())) { qCDebug(LOG_KTE) << "mode" << m_mode << "requires a different highlight style: highlighting '" << doc->highlight()->name() << "' (" << doc->highlight()->version() << "), style '" << doc->highlight()->style() << "'" ", but script require '" << m_script->indentHeader().requiredStyle() << "'" ; doc->config()->setIndentationMode(MODE_NORMAL()); } } } void KateAutoIndent::updateConfig() { KateDocumentConfig *config = doc->config(); useSpaces = config->replaceTabsDyn(); keepExtra = config->keepExtraSpaces(); tabWidth = config->tabWidth(); indentWidth = config->indentationWidth(); } bool KateAutoIndent::changeIndent(const KTextEditor::Range &range, int change) { QList skippedLines; // loop over all lines given... for (int line = range.start().line() < 0 ? 0 : range.start().line(); line <= qMin(range.end().line(), doc->lines() - 1); ++line) { // don't indent empty lines if (doc->line(line).isEmpty()) { skippedLines.append(line); continue; } // don't indent the last line when the cursor is on the first column if (line == range.end().line() && range.end().column() == 0) { skippedLines.append(line); continue; } doIndentRelative(line, change * indentWidth); } if (skippedLines.count() > range.numberOfLines()) { // all lines were empty, so indent them nevertheless foreach (int line, skippedLines) { doIndentRelative(line, change * indentWidth); } } return true; } void KateAutoIndent::indent(KTextEditor::ViewPrivate *view, const KTextEditor::Range &range) { // no script, do nothing... if (!m_script) { return; } // we want one undo action >= START doc->setUndoMergeAllEdits(true); // loop over all lines given... for (int line = range.start().line() < 0 ? 0 : range.start().line(); line <= qMin(range.end().line(), doc->lines() - 1); ++line) { // let the script indent for us... scriptIndent(view, KTextEditor::Cursor(line, 0), QChar()); } // we want one undo action => END doc->setUndoMergeAllEdits(false); } void KateAutoIndent::userTypedChar(KTextEditor::ViewPrivate *view, const KTextEditor::Cursor &position, QChar typedChar) { // normal mode if (m_mode == MODE_NORMAL()) { // only indent on new line, per default if (typedChar != QLatin1Char('\n')) { return; } // keep indent of previous line keepIndent(position.line()); return; } // no script, do nothing... if (!m_script) { return; } // does the script allow this char as trigger? if (typedChar != QLatin1Char('\n') && !m_script->triggerCharacters().contains(typedChar)) { return; } // let the script indent for us... scriptIndent(view, position, typedChar); } //END KateAutoIndent //BEGIN KateViewIndentAction KateViewIndentationAction::KateViewIndentationAction(KTextEditor::DocumentPrivate *_doc, const QString &text, QObject *parent) : KActionMenu(text, parent), doc(_doc) { + setDelayed(false); connect(menu(), SIGNAL(aboutToShow()), this, SLOT(slotAboutToShow())); actionGroup = new QActionGroup(menu()); } void KateViewIndentationAction::slotAboutToShow() { QStringList modes = KateAutoIndent::listModes(); menu()->clear(); foreach (QAction *action, actionGroup->actions()) { actionGroup->removeAction(action); } for (int z = 0; z < modes.size(); ++z) { QAction *action = menu()->addAction(QLatin1Char('&') + KateAutoIndent::modeDescription(z).replace(QLatin1Char('&'), QLatin1String("&&"))); actionGroup->addAction(action); action->setCheckable(true); action->setData(z); QString requiredStyle = KateAutoIndent::modeRequiredStyle(z); action->setEnabled(requiredStyle.isEmpty() || requiredStyle == doc->highlight()->style()); if (doc->config()->indentationMode() == KateAutoIndent::modeName(z)) { action->setChecked(true); } } disconnect(menu(), SIGNAL(triggered(QAction*)), this, SLOT(setMode(QAction*))); connect(menu(), SIGNAL(triggered(QAction*)), this, SLOT(setMode(QAction*))); } void KateViewIndentationAction::setMode(QAction *action) { // set new mode doc->config()->setIndentationMode(KateAutoIndent::modeName(action->data().toInt())); doc->rememberUserDidSetIndentationMode(); } //END KateViewIndentationAction diff --git a/src/utils/katebookmarks.cpp b/src/utils/katebookmarks.cpp index 771a9adb..a900fcbd 100644 --- a/src/utils/katebookmarks.cpp +++ b/src/utils/katebookmarks.cpp @@ -1,300 +1,301 @@ /* This file is part of the KDE libraries Copyright (C) 2002, 2003, 2004 Anders Lund Copyright (C) 2002 John Firebaugh 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 "katebookmarks.h" #include "katedocument.h" #include "kateview.h" #include "kateabstractinputmode.h" #include #include #include #include #include #include #include #include #include #include #include namespace KTextEditor { class Document; } KateBookmarks::KateBookmarks(KTextEditor::ViewPrivate *view, Sorting sort) : QObject(view) , m_view(view) , m_bookmarkClear(nullptr) , m_sorting(sort) { setObjectName(QStringLiteral("kate bookmarks")); connect(view->doc(), SIGNAL(marksChanged(KTextEditor::Document*)), this, SLOT(marksChanged())); _tries = 0; m_bookmarksMenu = nullptr; } KateBookmarks::~KateBookmarks() { } void KateBookmarks::createActions(KActionCollection *ac) { m_bookmarkToggle = new KToggleAction(i18n("Set &Bookmark"), this); ac->addAction(QStringLiteral("bookmarks_toggle"), m_bookmarkToggle); m_bookmarkToggle->setIcon(QIcon::fromTheme(QStringLiteral("bookmark-new"))); ac->setDefaultShortcut(m_bookmarkToggle, Qt::CTRL + Qt::Key_B); m_bookmarkToggle->setWhatsThis(i18n("If a line has no bookmark then add one, otherwise remove it.")); connect(m_bookmarkToggle, SIGNAL(triggered()), this, SLOT(toggleBookmark())); m_bookmarkClear = new QAction(i18n("Clear &All Bookmarks"), this); ac->addAction(QStringLiteral("bookmarks_clear"), m_bookmarkClear); m_bookmarkClear->setIcon(QIcon::fromTheme(QStringLiteral("bookmark-remove"))); m_bookmarkClear->setWhatsThis(i18n("Remove all bookmarks of the current document.")); connect(m_bookmarkClear, SIGNAL(triggered()), this, SLOT(clearBookmarks())); m_goNext = new QAction(i18n("Next Bookmark"), this); ac->addAction(QStringLiteral("bookmarks_next"), m_goNext); m_goNext->setIcon(QIcon::fromTheme(QStringLiteral("go-down-search"))); ac->setDefaultShortcut(m_goNext, Qt::ALT + Qt::Key_PageDown); m_goNext->setWhatsThis(i18n("Go to the next bookmark.")); connect(m_goNext, SIGNAL(triggered()), this, SLOT(goNext())); m_goPrevious = new QAction(i18n("Previous Bookmark"), this); ac->addAction(QStringLiteral("bookmarks_previous"), m_goPrevious); m_goPrevious->setIcon(QIcon::fromTheme(QStringLiteral("go-up-search"))); ac->setDefaultShortcut(m_goPrevious, Qt::ALT + Qt::Key_PageUp); m_goPrevious->setWhatsThis(i18n("Go to the previous bookmark.")); connect(m_goPrevious, SIGNAL(triggered()), this, SLOT(goPrevious())); KActionMenu *actionMenu = new KActionMenu(i18n("&Bookmarks"), this); ac->addAction(QStringLiteral("bookmarks"), actionMenu); + actionMenu->setDelayed(false); m_bookmarksMenu = actionMenu->menu(); connect(m_bookmarksMenu, SIGNAL(aboutToShow()), this, SLOT(bookmarkMenuAboutToShow())); marksChanged(); // Always want the actions with shortcuts plugged into something so their shortcuts can work m_view->addAction(m_bookmarkToggle); m_view->addAction(m_bookmarkClear); m_view->addAction(m_goNext); m_view->addAction(m_goPrevious); } void KateBookmarks::toggleBookmark() { uint mark = m_view->doc()->mark(m_view->cursorPosition().line()); if (mark & KTextEditor::MarkInterface::markType01) m_view->doc()->removeMark(m_view->cursorPosition().line(), KTextEditor::MarkInterface::markType01); else m_view->doc()->addMark(m_view->cursorPosition().line(), KTextEditor::MarkInterface::markType01); } void KateBookmarks::clearBookmarks() { QHash m = m_view->doc()->marks(); for (QHash::const_iterator i = m.constBegin(); i != m.constEnd(); ++i) { m_view->doc()->removeMark(i.value()->line, KTextEditor::MarkInterface::markType01); } // just to be sure ;) // dominik: the following line can be deleted afaics, as Document::removeMark emits this signal. marksChanged(); } void KateBookmarks::insertBookmarks(QMenu &menu) { int line = m_view->cursorPosition().line(); const QRegExp re(QLatin1String("&(?!&)")); int next = -1; // -1 means next bookmark doesn't exist int prev = -1; // -1 means previous bookmark doesn't exist const QHash &m = m_view->doc()->marks(); QVector bookmarkLineArray; // Array of line numbers which have bookmarks if (m.isEmpty()) { return; } // Find line numbers where bookmarks are set & store those line numbers in bookmarkLineArray for (QHash::const_iterator it = m.constBegin(); it != m.constEnd(); ++it) { if (it.value()->type & KTextEditor::MarkInterface::markType01) { bookmarkLineArray.append(it.value()->line); } } if (m_sorting == Position) { qSort(bookmarkLineArray.begin(), bookmarkLineArray.end()); } QAction *firstNewAction = menu.addSeparator(); // Consider each line with a bookmark one at a time for (int i = 0; i < bookmarkLineArray.size(); ++i) { // Get text in this particular line in a QString QString bText = menu.fontMetrics().elidedText (m_view->doc()->line(bookmarkLineArray.at(i)), Qt::ElideRight, menu.fontMetrics().maxWidth() * 32); bText.replace(re, QStringLiteral("&&")); // kill undesired accellerators! bText.replace(QLatin1Char('\t'), QLatin1Char(' ')); // kill tabs, as they are interpreted as shortcuts QAction *before = nullptr; if (m_sorting == Position) { // 3 actions already present if (menu.actions().size() <= i + 3) { before = nullptr; } else { before = menu.actions().at(i + 3); } } // Adding action for this bookmark in menu if (before) { QAction *a = new QAction(QStringLiteral("%1 %3 - \"%2\"") .arg(bookmarkLineArray.at(i) + 1).arg(bText) .arg(m_view->currentInputMode()->bookmarkLabel(bookmarkLineArray.at(i))), &menu); menu.insertAction(before, a); connect(a, SIGNAL(activated()), this, SLOT(gotoLine())); a->setData(bookmarkLineArray.at(i)); if (!firstNewAction) { firstNewAction = a; } } else { QAction *a = menu.addAction(QStringLiteral("%1 %3 - \"%2\"") .arg(bookmarkLineArray.at(i) + 1).arg(bText) .arg(m_view->currentInputMode()->bookmarkLabel(bookmarkLineArray.at(i))), this, SLOT(gotoLine())); a->setData(bookmarkLineArray.at(i)); } // Find the line number of previous & next bookmark (if present) in relation to the cursor if (bookmarkLineArray.at(i) < line) { if ((prev == -1) || prev < (bookmarkLineArray.at(i))) { prev = bookmarkLineArray.at(i); } } else if (bookmarkLineArray.at(i) > line) { if ((next == -1) || next > (bookmarkLineArray.at(i))) { next = bookmarkLineArray.at(i); } } } if (next != -1) { // Insert action for next bookmark m_goNext->setText(i18n("&Next: %1 - \"%2\"", next + 1, KStringHandler::rsqueeze(m_view->doc()->line(next), 24))); menu.insertAction(firstNewAction, m_goNext); firstNewAction = m_goNext; } if (prev != -1) { // Insert action for previous bookmark m_goPrevious->setText(i18n("&Previous: %1 - \"%2\"", prev + 1, KStringHandler::rsqueeze(m_view->doc()->line(prev), 24))); menu.insertAction(firstNewAction, m_goPrevious); firstNewAction = m_goPrevious; } if (next != -1 || prev != -1) { menu.insertSeparator(firstNewAction); } } void KateBookmarks::gotoLine() { if (!sender()) { return; } gotoLine(((QAction *)(sender()))->data().toInt()); } void KateBookmarks::gotoLine(int line) { m_view->setCursorPosition(KTextEditor::Cursor(line, 0)); } void KateBookmarks::bookmarkMenuAboutToShow() { m_bookmarksMenu->clear(); m_bookmarkToggle->setChecked(m_view->doc()->mark(m_view->cursorPosition().line()) & KTextEditor::MarkInterface::markType01); m_bookmarksMenu->addAction(m_bookmarkToggle); m_bookmarksMenu->addAction(m_bookmarkClear); m_goNext->setText(i18n("Next Bookmark")); m_goPrevious->setText(i18n("Previous Bookmark")); insertBookmarks(*m_bookmarksMenu); } void KateBookmarks::goNext() { const QHash &m = m_view->doc()->marks(); if (m.isEmpty()) { return; } int line = m_view->cursorPosition().line(); int found = -1; for (QHash::const_iterator it = m.constBegin(); it != m.constEnd(); ++it) { if ((it.value()->line > line) && ((found == -1) || (found > it.value()->line))) { found = it.value()->line; } } if (found != -1) { gotoLine(found); } } void KateBookmarks::goPrevious() { const QHash &m = m_view->doc()->marks(); if (m.isEmpty()) { return; } int line = m_view->cursorPosition().line(); int found = -1; for (QHash::const_iterator it = m.constBegin(); it != m.constEnd(); ++it) { if ((it.value()->line < line) && ((found == -1) || (found < it.value()->line))) { found = it.value()->line; } } if (found != -1) { gotoLine(found); } } void KateBookmarks::marksChanged() { if (m_bookmarkClear) { m_bookmarkClear->setEnabled(!m_view->doc()->marks().isEmpty()); } }