2012-10-02 09:12:39 +02:00
|
|
|
/****************************************************************************
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2016-01-15 14:57:40 +01:00
|
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** This file is part of Qt Creator.
|
2008-12-02 12:01:29 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
** Commercial License Usage
|
|
|
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
|
|
|
** accordance with the commercial license agreement provided with the
|
|
|
|
|
** Software or, alternatively, in accordance with the terms contained in
|
2016-01-15 14:57:40 +01:00
|
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
|
|
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
|
|
|
|
** information use the contact form at https://www.qt.io/contact-us.
|
2008-12-02 14:17:16 +01:00
|
|
|
**
|
2016-01-15 14:57:40 +01:00
|
|
|
** GNU General Public License Usage
|
|
|
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
|
|
|
** General Public License version 3 as published by the Free Software
|
|
|
|
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
|
|
|
|
** included in the packaging of this file. Please review the following
|
|
|
|
|
** information to ensure the GNU General Public License requirements will
|
|
|
|
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
2010-12-17 16:01:08 +01:00
|
|
|
**
|
2012-10-02 09:12:39 +02:00
|
|
|
****************************************************************************/
|
2008-12-02 16:19:05 +01:00
|
|
|
|
2014-09-26 09:14:03 +02:00
|
|
|
#include "texteditor.h"
|
|
|
|
|
#include "texteditor_p.h"
|
2015-02-26 13:22:35 +01:00
|
|
|
#include "displaysettings.h"
|
|
|
|
|
#include "marginsettings.h"
|
|
|
|
|
#include "fontsettings.h"
|
|
|
|
|
#include "texteditoractionhandler.h"
|
2008-12-02 16:19:05 +01:00
|
|
|
|
2014-09-26 09:14:03 +02:00
|
|
|
#include "autocompleter.h"
|
2014-09-30 13:08:05 +02:00
|
|
|
#include "basehoverhandler.h"
|
2010-02-04 10:33:12 +01:00
|
|
|
#include "behaviorsettings.h"
|
2014-09-26 09:14:03 +02:00
|
|
|
#include "circularclipboard.h"
|
|
|
|
|
#include "circularclipboardassist.h"
|
2008-12-02 12:01:29 +01:00
|
|
|
#include "codecselector.h"
|
2010-05-11 11:33:31 +02:00
|
|
|
#include "completionsettings.h"
|
2014-09-26 09:14:03 +02:00
|
|
|
#include "highlighterutils.h"
|
2011-08-16 10:45:23 +02:00
|
|
|
#include "icodestylepreferences.h"
|
2010-11-05 14:27:16 +01:00
|
|
|
#include "indenter.h"
|
2014-09-26 09:14:03 +02:00
|
|
|
#include "snippets/snippet.h"
|
|
|
|
|
#include "syntaxhighlighter.h"
|
|
|
|
|
#include "tabsettings.h"
|
|
|
|
|
#include "textdocument.h"
|
|
|
|
|
#include "textdocumentlayout.h"
|
2017-07-21 14:31:21 +02:00
|
|
|
#include "texteditorconstants.h"
|
2011-08-18 14:23:06 +02:00
|
|
|
#include "texteditoroverlay.h"
|
2015-02-26 13:22:35 +01:00
|
|
|
#include "refactoroverlay.h"
|
2014-09-26 09:14:03 +02:00
|
|
|
#include "texteditorsettings.h"
|
|
|
|
|
#include "typingsettings.h"
|
2015-02-13 19:06:16 +02:00
|
|
|
#include "extraencodingsettings.h"
|
|
|
|
|
#include "storagesettings.h"
|
2014-08-22 12:14:06 +02:00
|
|
|
|
2014-09-04 00:04:18 +02:00
|
|
|
#include <texteditor/codeassist/assistinterface.h>
|
2014-10-01 22:39:47 +02:00
|
|
|
#include <texteditor/codeassist/codeassistant.h>
|
|
|
|
|
#include <texteditor/codeassist/completionassistprovider.h>
|
2017-07-26 10:53:37 +02:00
|
|
|
#include <texteditor/codeassist/keywordscompletionassist.h>
|
2014-07-30 10:04:01 +02:00
|
|
|
#include <texteditor/generichighlighter/context.h>
|
|
|
|
|
#include <texteditor/generichighlighter/highlightdefinition.h>
|
|
|
|
|
#include <texteditor/generichighlighter/highlighter.h>
|
|
|
|
|
#include <texteditor/generichighlighter/highlightersettings.h>
|
|
|
|
|
#include <texteditor/generichighlighter/manager.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2014-07-30 10:04:01 +02:00
|
|
|
#include <coreplugin/icore.h>
|
2009-07-15 12:38:45 +02:00
|
|
|
#include <aggregation/aggregate.h>
|
2010-02-04 10:33:12 +01:00
|
|
|
#include <coreplugin/actionmanager/actionmanager.h>
|
2010-03-18 10:59:06 +01:00
|
|
|
#include <coreplugin/actionmanager/actioncontainer.h>
|
2015-02-26 13:38:54 +01:00
|
|
|
#include <coreplugin/actionmanager/command.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <coreplugin/coreconstants.h>
|
2013-04-15 18:19:34 +02:00
|
|
|
#include <coreplugin/editormanager/editormanager.h>
|
2011-05-06 12:48:44 +02:00
|
|
|
#include <coreplugin/infobar.h>
|
2010-02-04 10:33:12 +01:00
|
|
|
#include <coreplugin/manhattanstyle.h>
|
2014-01-13 16:17:34 +01:00
|
|
|
#include <coreplugin/find/basetextfind.h>
|
2015-06-04 10:43:34 +02:00
|
|
|
#include <coreplugin/find/highlightscrollbar.h>
|
2017-06-20 08:28:10 +02:00
|
|
|
#include <utils/algorithm.h>
|
2017-06-26 08:17:29 +02:00
|
|
|
#include <utils/asconst.h>
|
2017-09-21 12:35:24 +02:00
|
|
|
#include <utils/textutils.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
#include <utils/linecolumnlabel.h>
|
2014-10-15 13:27:22 +02:00
|
|
|
#include <utils/fileutils.h>
|
2015-06-04 12:35:59 +02:00
|
|
|
#include <utils/dropsupport.h>
|
2015-12-18 10:28:00 +02:00
|
|
|
#include <utils/fadingindicator.h>
|
2015-06-04 10:43:34 +02:00
|
|
|
#include <utils/filesearch.h>
|
2012-08-23 15:53:58 +02:00
|
|
|
#include <utils/hostosinfo.h>
|
2015-02-04 09:32:46 +01:00
|
|
|
#include <utils/mimetypes/mimedatabase.h>
|
2008-12-09 15:25:01 +01:00
|
|
|
#include <utils/qtcassert.h>
|
2010-02-04 10:33:12 +01:00
|
|
|
#include <utils/stylehelper.h>
|
2013-01-10 15:07:17 +01:00
|
|
|
#include <utils/tooltip/tooltip.h>
|
2013-03-06 15:28:19 +01:00
|
|
|
#include <utils/uncommentselection.h>
|
2015-06-04 10:43:34 +02:00
|
|
|
#include <utils/theme/theme.h>
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QAbstractTextDocumentLayout>
|
|
|
|
|
#include <QApplication>
|
2014-08-19 14:23:03 +02:00
|
|
|
#include <QClipboard>
|
|
|
|
|
#include <QCoreApplication>
|
|
|
|
|
#include <QDebug>
|
2016-08-16 14:19:41 +02:00
|
|
|
#include <QGridLayout>
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QKeyEvent>
|
2017-10-06 14:29:13 +02:00
|
|
|
#include <QMap>
|
2014-08-19 14:23:03 +02:00
|
|
|
#include <QMenu>
|
|
|
|
|
#include <QMessageBox>
|
|
|
|
|
#include <QMimeData>
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QPainter>
|
|
|
|
|
#include <QPrintDialog>
|
2014-08-19 14:23:03 +02:00
|
|
|
#include <QPrinter>
|
2017-05-23 14:34:22 +02:00
|
|
|
#include <QPropertyAnimation>
|
2014-10-15 13:27:22 +02:00
|
|
|
#include <QDrag>
|
2017-05-23 14:34:22 +02:00
|
|
|
#include <QSequentialAnimationGroup>
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QScrollBar>
|
|
|
|
|
#include <QShortcut>
|
|
|
|
|
#include <QStyle>
|
2014-08-19 14:23:03 +02:00
|
|
|
#include <QTextBlock>
|
|
|
|
|
#include <QTextCodec>
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QTextCursor>
|
|
|
|
|
#include <QTextDocumentFragment>
|
|
|
|
|
#include <QTextLayout>
|
2014-08-19 14:23:03 +02:00
|
|
|
#include <QTime>
|
|
|
|
|
#include <QTimeLine>
|
|
|
|
|
#include <QTimer>
|
2012-02-15 10:42:41 +01:00
|
|
|
#include <QToolBar>
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2011-05-26 18:09:17 +02:00
|
|
|
/*!
|
|
|
|
|
\namespace TextEditor
|
|
|
|
|
\brief The TextEditor namespace contains the base text editor and several classes which
|
|
|
|
|
provide supporting functionality like snippets, highlighting, \l{CodeAssist}{code assist},
|
|
|
|
|
indentation and style, and others.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
|
\namespace TextEditor::Internal
|
|
|
|
|
\internal
|
|
|
|
|
*/
|
|
|
|
|
|
2014-01-21 14:22:33 +01:00
|
|
|
/*!
|
|
|
|
|
\class TextEditor::BaseTextEditor
|
2014-07-30 13:13:24 +02:00
|
|
|
\brief The BaseTextEditor class is base implementation for QPlainTextEdit-based
|
|
|
|
|
text editors. It can use the Kate text highlighting definitions, and some basic
|
|
|
|
|
auto indentation.
|
|
|
|
|
|
|
|
|
|
The corresponding document base class is BaseTextDocument, the corresponding
|
|
|
|
|
widget base class is BaseTextEditorWidget.
|
2014-01-21 14:22:33 +01:00
|
|
|
|
2014-07-30 13:13:24 +02:00
|
|
|
It is the default editor for text files used by \QC, if no other editor
|
|
|
|
|
implementation matches the MIME type.
|
2014-01-21 14:22:33 +01:00
|
|
|
*/
|
|
|
|
|
|
2014-07-25 14:10:41 +02:00
|
|
|
|
2014-01-13 16:17:34 +01:00
|
|
|
using namespace Core;
|
2012-08-23 15:53:58 +02:00
|
|
|
using namespace Utils;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
namespace TextEditor {
|
2009-01-20 11:52:04 +01:00
|
|
|
namespace Internal {
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2015-06-23 15:56:45 +02:00
|
|
|
enum { NExtraSelectionKinds = 12 };
|
|
|
|
|
|
2014-07-29 22:02:56 -07:00
|
|
|
typedef QString (TransformationMethod)(const QString &);
|
2017-08-13 20:38:08 +03:00
|
|
|
typedef void (ListTransformationMethod)(QStringList &);
|
2014-07-29 22:02:56 -07:00
|
|
|
|
|
|
|
|
static QString QString_toUpper(const QString &str)
|
|
|
|
|
{
|
|
|
|
|
return str.toUpper();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static QString QString_toLower(const QString &str)
|
|
|
|
|
{
|
|
|
|
|
return str.toLower();
|
|
|
|
|
}
|
2014-07-25 16:23:50 +02:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
class TextEditorAnimator : public QObject
|
2014-07-23 19:10:38 +02:00
|
|
|
{
|
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
|
|
public:
|
2014-09-26 11:37:54 +02:00
|
|
|
TextEditorAnimator(QObject *parent);
|
2014-07-23 19:10:38 +02:00
|
|
|
|
2016-05-03 10:59:30 +02:00
|
|
|
void init(const QTextCursor &cursor, const QFont &f, const QPalette &pal);
|
|
|
|
|
inline QTextCursor cursor() const { return m_cursor; }
|
2014-07-23 19:10:38 +02:00
|
|
|
|
|
|
|
|
void draw(QPainter *p, const QPointF &pos);
|
|
|
|
|
QRectF rect() const;
|
|
|
|
|
|
|
|
|
|
inline qreal value() const { return m_value; }
|
|
|
|
|
inline QPointF lastDrawPos() const { return m_lastDrawPos; }
|
|
|
|
|
|
|
|
|
|
void finish();
|
|
|
|
|
|
|
|
|
|
bool isRunning() const;
|
|
|
|
|
|
|
|
|
|
signals:
|
2016-05-03 10:59:30 +02:00
|
|
|
void updateRequest(const QTextCursor &cursor, QPointF lastPos, QRectF rect);
|
2014-07-23 19:10:38 +02:00
|
|
|
|
2014-08-20 22:01:53 +02:00
|
|
|
private:
|
2014-07-23 19:10:38 +02:00
|
|
|
void step(qreal v);
|
|
|
|
|
|
2014-08-20 22:01:53 +02:00
|
|
|
QTimeLine m_timeline;
|
2014-07-23 19:10:38 +02:00
|
|
|
qreal m_value;
|
2016-05-03 10:59:30 +02:00
|
|
|
QTextCursor m_cursor;
|
2014-07-23 19:10:38 +02:00
|
|
|
QPointF m_lastDrawPos;
|
|
|
|
|
QFont m_font;
|
|
|
|
|
QPalette m_palette;
|
|
|
|
|
QString m_text;
|
|
|
|
|
QSizeF m_size;
|
|
|
|
|
};
|
|
|
|
|
|
2014-08-20 23:04:08 +02:00
|
|
|
class TextEditExtraArea : public QWidget
|
|
|
|
|
{
|
|
|
|
|
public:
|
2014-09-26 11:37:54 +02:00
|
|
|
TextEditExtraArea(TextEditorWidget *edit)
|
2014-08-20 23:04:08 +02:00
|
|
|
: QWidget(edit)
|
|
|
|
|
{
|
|
|
|
|
textEdit = edit;
|
|
|
|
|
setAutoFillBackground(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
|
|
|
|
QSize sizeHint() const {
|
|
|
|
|
return QSize(textEdit->extraAreaWidth(), 0);
|
|
|
|
|
}
|
|
|
|
|
void paintEvent(QPaintEvent *event) {
|
|
|
|
|
textEdit->extraAreaPaintEvent(event);
|
|
|
|
|
}
|
|
|
|
|
void mousePressEvent(QMouseEvent *event) {
|
|
|
|
|
textEdit->extraAreaMouseEvent(event);
|
|
|
|
|
}
|
|
|
|
|
void mouseMoveEvent(QMouseEvent *event) {
|
|
|
|
|
textEdit->extraAreaMouseEvent(event);
|
|
|
|
|
}
|
|
|
|
|
void mouseReleaseEvent(QMouseEvent *event) {
|
|
|
|
|
textEdit->extraAreaMouseEvent(event);
|
|
|
|
|
}
|
|
|
|
|
void leaveEvent(QEvent *event) {
|
|
|
|
|
textEdit->extraAreaLeaveEvent(event);
|
|
|
|
|
}
|
|
|
|
|
void contextMenuEvent(QContextMenuEvent *event) {
|
|
|
|
|
textEdit->extraAreaContextMenuEvent(event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void wheelEvent(QWheelEvent *event) {
|
|
|
|
|
QCoreApplication::sendEvent(textEdit->viewport(), event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
2014-09-26 11:37:54 +02:00
|
|
|
TextEditorWidget *textEdit;
|
2014-08-20 23:04:08 +02:00
|
|
|
};
|
|
|
|
|
|
2014-07-26 12:45:48 +02:00
|
|
|
class BaseTextEditorPrivate
|
|
|
|
|
{
|
|
|
|
|
public:
|
2014-08-22 12:14:06 +02:00
|
|
|
BaseTextEditorPrivate() {}
|
2014-07-31 14:44:35 +02:00
|
|
|
|
2017-07-18 23:15:52 +02:00
|
|
|
TextEditorFactoryPrivate *m_origin = nullptr;
|
2014-07-26 12:45:48 +02:00
|
|
|
};
|
|
|
|
|
|
2017-06-26 11:15:19 +02:00
|
|
|
class HoverHandlerRunner
|
|
|
|
|
{
|
|
|
|
|
public:
|
|
|
|
|
HoverHandlerRunner(TextEditorWidget *widget, QList<BaseHoverHandler *> &handlers)
|
|
|
|
|
: m_widget(widget)
|
|
|
|
|
, m_handlers(handlers)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void startChecking(const QTextCursor &textCursor, const QPoint &point)
|
|
|
|
|
{
|
|
|
|
|
if (m_handlers.empty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Does the last handler still applies?
|
|
|
|
|
const int documentRevision = textCursor.document()->revision();
|
2017-09-21 12:35:24 +02:00
|
|
|
const int position = Text::wordStartCursor(textCursor).position();
|
2017-06-26 11:15:19 +02:00
|
|
|
if (m_lastHandlerInfo.applies(documentRevision, position)) {
|
|
|
|
|
m_lastHandlerInfo.handler->showToolTip(m_widget, point, /*decorate=*/ false);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Cancel currently running checks
|
|
|
|
|
for (BaseHoverHandler *handler : m_handlers) {
|
|
|
|
|
if (handler->isAsyncHandler())
|
|
|
|
|
handler->cancelAsyncCheck();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Update invocation data
|
|
|
|
|
m_documentRevision = documentRevision;
|
|
|
|
|
m_position = position;
|
|
|
|
|
m_point = point;
|
|
|
|
|
|
|
|
|
|
// Re-initialize process data
|
|
|
|
|
m_currentHandlerIndex = 0;
|
|
|
|
|
m_bestHandler = nullptr;
|
|
|
|
|
m_highestHandlerPriority = -1;
|
|
|
|
|
|
|
|
|
|
// Start checking
|
|
|
|
|
checkNext();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void checkNext()
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(m_currentHandlerIndex < m_handlers.size(), return);
|
|
|
|
|
BaseHoverHandler *currentHandler = m_handlers[m_currentHandlerIndex];
|
|
|
|
|
|
|
|
|
|
currentHandler->checkPriority(m_widget, m_position, [this](int priority) {
|
|
|
|
|
onHandlerFinished(m_documentRevision, m_position, priority);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void onHandlerFinished(int documentRevision, int position, int priority)
|
|
|
|
|
{
|
|
|
|
|
QTC_ASSERT(m_currentHandlerIndex < m_handlers.size(), return);
|
|
|
|
|
QTC_ASSERT(documentRevision == m_documentRevision, return);
|
|
|
|
|
QTC_ASSERT(position == m_position, return);
|
|
|
|
|
|
|
|
|
|
BaseHoverHandler *currentHandler = m_handlers[m_currentHandlerIndex];
|
|
|
|
|
if (priority > m_highestHandlerPriority) {
|
|
|
|
|
m_highestHandlerPriority = priority;
|
|
|
|
|
m_bestHandler = currentHandler;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// There are more, check next
|
|
|
|
|
++m_currentHandlerIndex;
|
|
|
|
|
if (m_currentHandlerIndex < m_handlers.size()) {
|
|
|
|
|
checkNext();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// All were queried, run the best
|
|
|
|
|
if (m_bestHandler) {
|
|
|
|
|
m_lastHandlerInfo = LastHandlerInfo(m_bestHandler, m_documentRevision, m_position);
|
|
|
|
|
m_bestHandler->showToolTip(m_widget, m_point);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
TextEditorWidget *m_widget = nullptr;
|
|
|
|
|
const QList<BaseHoverHandler *> &m_handlers;
|
|
|
|
|
|
|
|
|
|
struct LastHandlerInfo {
|
|
|
|
|
LastHandlerInfo() = default;
|
|
|
|
|
LastHandlerInfo(BaseHoverHandler *handler, int documentRevision, int cursorPosition)
|
|
|
|
|
: handler(handler)
|
|
|
|
|
, documentRevision(documentRevision)
|
|
|
|
|
, cursorPosition(cursorPosition)
|
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
bool applies(int documentRevision, int cursorPosition) const
|
|
|
|
|
{
|
|
|
|
|
return handler
|
|
|
|
|
&& documentRevision == this->documentRevision
|
|
|
|
|
&& cursorPosition == this->cursorPosition;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseHoverHandler *handler = nullptr;
|
|
|
|
|
int documentRevision = -1;
|
|
|
|
|
int cursorPosition = -1;
|
|
|
|
|
} m_lastHandlerInfo;
|
|
|
|
|
|
|
|
|
|
// invocation data
|
|
|
|
|
QPoint m_point;
|
|
|
|
|
int m_position = -1;
|
|
|
|
|
int m_documentRevision = -1;
|
|
|
|
|
|
|
|
|
|
// processing data
|
|
|
|
|
int m_currentHandlerIndex = -1;
|
|
|
|
|
int m_highestHandlerPriority = -1;
|
|
|
|
|
BaseHoverHandler *m_bestHandler = nullptr;
|
|
|
|
|
};
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
class TextEditorWidgetPrivate : public QObject
|
2014-07-23 19:10:38 +02:00
|
|
|
{
|
|
|
|
|
public:
|
2014-09-26 11:37:54 +02:00
|
|
|
TextEditorWidgetPrivate(TextEditorWidget *parent);
|
2015-09-29 15:15:03 +02:00
|
|
|
~TextEditorWidgetPrivate();
|
2014-07-23 19:10:38 +02:00
|
|
|
|
|
|
|
|
void setupDocumentSignals();
|
|
|
|
|
void updateLineSelectionColor();
|
|
|
|
|
|
|
|
|
|
void print(QPrinter *printer);
|
|
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
void maybeSelectLine();
|
2016-09-26 15:55:04 +02:00
|
|
|
void duplicateSelection(bool comment);
|
|
|
|
|
void duplicateBlockSelection(bool comment);
|
2014-07-25 16:23:50 +02:00
|
|
|
void updateCannotDecodeInfo();
|
|
|
|
|
void collectToCircularClipboard();
|
|
|
|
|
|
2014-09-22 18:43:31 +02:00
|
|
|
void ctor(const QSharedPointer<TextDocument> &doc);
|
2014-07-25 16:23:50 +02:00
|
|
|
void handleHomeKey(bool anchor);
|
|
|
|
|
void handleBackspaceKey();
|
|
|
|
|
void moveLineUpDown(bool up);
|
|
|
|
|
void copyLineUpDown(bool up);
|
|
|
|
|
void saveCurrentCursorPositionForNavigation();
|
|
|
|
|
void updateHighlights();
|
2015-06-04 10:43:34 +02:00
|
|
|
void updateCurrentLineInScrollbar();
|
2014-07-25 16:23:50 +02:00
|
|
|
void updateCurrentLineHighlight();
|
|
|
|
|
|
|
|
|
|
void drawFoldingMarker(QPainter *painter, const QPalette &pal,
|
|
|
|
|
const QRect &rect,
|
|
|
|
|
bool expanded,
|
|
|
|
|
bool active,
|
|
|
|
|
bool hovered) const;
|
2017-10-17 08:23:41 +02:00
|
|
|
void updateLineAnnotation(QPainter &painter, const QTextBlock &block, qreal start, const QRect &eventRect);
|
2014-07-25 16:23:50 +02:00
|
|
|
|
|
|
|
|
void toggleBlockVisible(const QTextBlock &block);
|
|
|
|
|
QRect foldBox();
|
|
|
|
|
|
|
|
|
|
QTextBlock foldedBlockAt(const QPoint &pos, QRect *box = 0) const;
|
|
|
|
|
|
2015-09-21 10:27:43 +02:00
|
|
|
void requestUpdateLink(QMouseEvent *e, bool immediate = false);
|
|
|
|
|
void updateLink();
|
2014-09-26 11:37:54 +02:00
|
|
|
void showLink(const TextEditorWidget::Link &);
|
2014-07-25 16:23:50 +02:00
|
|
|
void clearLink();
|
|
|
|
|
|
|
|
|
|
void universalHelper(); // test function for development
|
|
|
|
|
|
|
|
|
|
bool cursorMoveKeyEvent(QKeyEvent *e);
|
|
|
|
|
bool camelCaseRight(QTextCursor &cursor, QTextCursor::MoveMode mode);
|
|
|
|
|
bool camelCaseLeft(QTextCursor &cursor, QTextCursor::MoveMode mode);
|
|
|
|
|
|
|
|
|
|
void processTooltipRequest(const QTextCursor &c);
|
2017-06-20 08:28:10 +02:00
|
|
|
bool processAnnotaionTooltipRequest(const QTextBlock &block, const QPoint &pos) const;
|
2014-07-25 16:23:50 +02:00
|
|
|
|
2015-02-03 23:46:35 +02:00
|
|
|
void transformSelection(TransformationMethod method);
|
|
|
|
|
void transformBlockSelection(TransformationMethod method);
|
2014-07-25 16:23:50 +02:00
|
|
|
|
2017-08-13 20:38:08 +03:00
|
|
|
void transformSelectedLines(ListTransformationMethod method);
|
|
|
|
|
|
2014-08-01 15:06:28 +02:00
|
|
|
void slotUpdateExtraAreaWidth();
|
|
|
|
|
void slotUpdateRequest(const QRect &r, int dy);
|
|
|
|
|
void slotUpdateBlockNotify(const QTextBlock &);
|
|
|
|
|
void updateTabStops();
|
|
|
|
|
void applyFontSettingsDelayed();
|
2017-10-06 14:29:13 +02:00
|
|
|
void markRemoved(TextMark *mark);
|
2014-08-01 15:06:28 +02:00
|
|
|
|
|
|
|
|
void editorContentsChange(int position, int charsRemoved, int charsAdded);
|
|
|
|
|
void documentAboutToBeReloaded();
|
|
|
|
|
void documentReloadFinished(bool success);
|
2014-08-19 14:23:03 +02:00
|
|
|
void highlightSearchResultsSlot(const QString &txt, FindFlags findFlags);
|
2015-06-04 10:43:34 +02:00
|
|
|
void searchResultsReady(int beginIndex, int endIndex);
|
|
|
|
|
void searchFinished();
|
|
|
|
|
void setupScrollBar();
|
|
|
|
|
void highlightSearchResultsInScrollBar();
|
|
|
|
|
void scheduleUpdateHighlightScrollBar();
|
|
|
|
|
void updateHighlightScrollBarNow();
|
|
|
|
|
struct SearchResult {
|
|
|
|
|
int start;
|
|
|
|
|
int length;
|
|
|
|
|
};
|
|
|
|
|
void addSearchResultsToScrollBar(QVector<SearchResult> results);
|
|
|
|
|
void adjustScrollBarRanges();
|
|
|
|
|
|
2014-08-01 15:06:28 +02:00
|
|
|
void setFindScope(const QTextCursor &start, const QTextCursor &end, int, int);
|
|
|
|
|
|
2014-08-01 16:41:18 +02:00
|
|
|
void updateCursorPosition();
|
|
|
|
|
|
2014-08-01 15:06:28 +02:00
|
|
|
// parentheses matcher
|
|
|
|
|
void _q_matchParentheses();
|
|
|
|
|
void _q_highlightBlocks();
|
2016-06-01 10:19:59 +02:00
|
|
|
void autocompleterHighlight(const QTextCursor &cursor = QTextCursor());
|
2016-04-28 12:51:34 +02:00
|
|
|
void updateAnimator(QPointer<TextEditorAnimator> animator, QPainter &painter);
|
|
|
|
|
void cancelCurrentAnimations();
|
2014-08-01 15:06:28 +02:00
|
|
|
void slotSelectionChanged();
|
2016-05-03 10:59:30 +02:00
|
|
|
void _q_animateUpdate(const QTextCursor &cursor, QPointF lastPos, QRectF rect);
|
2014-08-01 15:06:28 +02:00
|
|
|
void updateCodeFoldingVisible();
|
|
|
|
|
|
2014-09-18 13:19:51 +02:00
|
|
|
void reconfigure();
|
|
|
|
|
|
2014-08-01 15:06:28 +02:00
|
|
|
public:
|
2014-09-26 11:37:54 +02:00
|
|
|
TextEditorWidget *q;
|
2017-01-14 22:48:24 +02:00
|
|
|
QToolBar *m_toolBar = nullptr;
|
|
|
|
|
QWidget *m_stretchWidget = nullptr;
|
|
|
|
|
LineColumnLabel *m_cursorPositionLabel = nullptr;
|
|
|
|
|
LineColumnLabel *m_fileEncodingLabel = nullptr;
|
|
|
|
|
QAction *m_cursorPositionLabelAction = nullptr;
|
|
|
|
|
QAction *m_fileEncodingLabelAction = nullptr;
|
2014-08-01 16:41:18 +02:00
|
|
|
|
2017-01-14 22:48:24 +02:00
|
|
|
bool m_contentsChanged = false;
|
|
|
|
|
bool m_lastCursorChangeWasInteresting = false;
|
2014-07-23 19:10:38 +02:00
|
|
|
|
2014-09-22 18:43:31 +02:00
|
|
|
QSharedPointer<TextDocument> m_document;
|
2014-07-23 19:10:38 +02:00
|
|
|
QByteArray m_tempState;
|
|
|
|
|
QByteArray m_tempNavigationState;
|
|
|
|
|
|
2017-01-14 22:48:24 +02:00
|
|
|
bool m_parenthesesMatchingEnabled = false;
|
2014-07-23 19:10:38 +02:00
|
|
|
|
|
|
|
|
// parentheses matcher
|
2017-01-14 22:48:24 +02:00
|
|
|
bool m_formatRange = false;
|
2014-07-23 19:10:38 +02:00
|
|
|
QTimer m_parenthesesMatchingTimer;
|
|
|
|
|
// end parentheses matcher
|
|
|
|
|
|
2017-01-14 22:48:24 +02:00
|
|
|
QWidget *m_extraArea = nullptr;
|
2014-07-23 19:10:38 +02:00
|
|
|
|
2014-08-19 14:23:03 +02:00
|
|
|
Id m_tabSettingsId;
|
2017-01-14 22:48:24 +02:00
|
|
|
ICodeStylePreferences *m_codeStylePreferences = nullptr;
|
2014-07-23 19:10:38 +02:00
|
|
|
DisplaySettings m_displaySettings;
|
2017-07-19 14:51:21 +02:00
|
|
|
bool m_annotationsrRight = true;
|
2014-07-23 19:10:38 +02:00
|
|
|
MarginSettings m_marginSettings;
|
2017-01-14 22:48:24 +02:00
|
|
|
// apply when making visible the first time, for the split case
|
|
|
|
|
bool m_fontSettingsNeedsApply = true;
|
2014-07-23 19:10:38 +02:00
|
|
|
BehaviorSettings m_behaviorSettings;
|
|
|
|
|
|
2017-01-14 22:48:24 +02:00
|
|
|
int extraAreaSelectionAnchorBlockNumber = -1;
|
|
|
|
|
int extraAreaToggleMarkBlockNumber = -1;
|
|
|
|
|
int extraAreaHighlightFoldedBlockNumber = -1;
|
|
|
|
|
int extraAreaPreviousMarkTooltipRequestedLine = -1;
|
2014-07-23 19:10:38 +02:00
|
|
|
|
2017-01-14 22:48:24 +02:00
|
|
|
TextEditorOverlay *m_overlay = nullptr;
|
|
|
|
|
TextEditorOverlay *m_snippetOverlay = nullptr;
|
|
|
|
|
TextEditorOverlay *m_searchResultOverlay = nullptr;
|
2014-07-23 19:10:38 +02:00
|
|
|
bool snippetCheckCursor(const QTextCursor &cursor);
|
|
|
|
|
void snippetTabOrBacktab(bool forward);
|
|
|
|
|
|
2017-06-20 08:28:10 +02:00
|
|
|
struct AnnotationRect
|
|
|
|
|
{
|
|
|
|
|
QRectF rect;
|
|
|
|
|
const TextMark *mark;
|
|
|
|
|
};
|
|
|
|
|
QMap<int, QList<AnnotationRect>> m_annotationRects;
|
2017-07-19 14:51:21 +02:00
|
|
|
QRectF getLastLineLineRect(const QTextBlock &block);
|
2017-06-20 08:28:10 +02:00
|
|
|
|
2017-01-14 22:48:24 +02:00
|
|
|
RefactorOverlay *m_refactorOverlay = nullptr;
|
2014-09-30 13:08:05 +02:00
|
|
|
QString m_contextHelpId;
|
2014-07-23 19:10:38 +02:00
|
|
|
|
|
|
|
|
QBasicTimer foldedBlockTimer;
|
2017-01-14 22:48:24 +02:00
|
|
|
int visibleFoldedBlockNumber = -1;
|
|
|
|
|
int suggestedVisibleFoldedBlockNumber = -1;
|
2014-07-23 19:10:38 +02:00
|
|
|
void clearVisibleFoldedBlock();
|
2017-01-14 22:48:24 +02:00
|
|
|
bool m_mouseOnFoldedMarker = false;
|
2014-07-23 19:10:38 +02:00
|
|
|
void foldLicenseHeader();
|
|
|
|
|
|
|
|
|
|
QBasicTimer autoScrollTimer;
|
|
|
|
|
uint m_marksVisible : 1;
|
|
|
|
|
uint m_codeFoldingVisible : 1;
|
|
|
|
|
uint m_codeFoldingSupported : 1;
|
|
|
|
|
uint m_revisionsVisible : 1;
|
|
|
|
|
uint m_lineNumbersVisible : 1;
|
|
|
|
|
uint m_highlightCurrentLine : 1;
|
|
|
|
|
uint m_requestMarkEnabled : 1;
|
|
|
|
|
uint m_lineSeparatorsAllowed : 1;
|
|
|
|
|
uint m_maybeFakeTooltipEvent : 1;
|
2017-01-14 22:48:24 +02:00
|
|
|
int m_visibleWrapColumn = 0;
|
2014-07-23 19:10:38 +02:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
TextEditorWidget::Link m_currentLink;
|
2017-01-14 22:48:24 +02:00
|
|
|
bool m_linkPressed = false;
|
2015-09-21 10:27:43 +02:00
|
|
|
QTextCursor m_pendingLinkUpdate;
|
|
|
|
|
QTextCursor m_lastLinkUpdate;
|
2014-07-23 19:10:38 +02:00
|
|
|
|
|
|
|
|
QRegExp m_searchExpr;
|
2014-08-19 14:23:03 +02:00
|
|
|
FindFlags m_findFlags;
|
2014-07-23 19:10:38 +02:00
|
|
|
void highlightSearchResults(const QTextBlock &block, TextEditorOverlay *overlay);
|
|
|
|
|
QTimer m_delayedUpdateTimer;
|
|
|
|
|
|
2015-06-23 15:56:45 +02:00
|
|
|
void setExtraSelections(Core::Id kind, const QList<QTextEdit::ExtraSelection> &selections);
|
|
|
|
|
QHash<Core::Id, QList<QTextEdit::ExtraSelection>> m_extraSelections;
|
2014-07-23 19:10:38 +02:00
|
|
|
|
|
|
|
|
// block selection mode
|
2017-01-14 22:48:24 +02:00
|
|
|
bool m_inBlockSelectionMode = false;
|
2014-07-23 19:10:38 +02:00
|
|
|
QString copyBlockSelection();
|
|
|
|
|
void insertIntoBlockSelection(const QString &text = QString());
|
|
|
|
|
void setCursorToColumn(QTextCursor &cursor, int column,
|
|
|
|
|
QTextCursor::MoveMode moveMode = QTextCursor::MoveAnchor);
|
|
|
|
|
void removeBlockSelection();
|
|
|
|
|
void enableBlockSelection(const QTextCursor &cursor);
|
|
|
|
|
void enableBlockSelection(int positionBlock, int positionColumn,
|
|
|
|
|
int anchorBlock, int anchorColumn);
|
2016-12-21 08:44:10 +01:00
|
|
|
|
|
|
|
|
enum BlockSelectionUpdateKind {
|
|
|
|
|
NoCursorUpdate,
|
|
|
|
|
CursorUpdateKeepSelection,
|
|
|
|
|
CursorUpdateClearSelection,
|
|
|
|
|
};
|
|
|
|
|
void disableBlockSelection(BlockSelectionUpdateKind kind);
|
2014-07-23 19:10:38 +02:00
|
|
|
void resetCursorFlashTimer();
|
|
|
|
|
QBasicTimer m_cursorFlashTimer;
|
2017-07-18 23:15:52 +02:00
|
|
|
bool m_cursorVisible = true;
|
2017-01-14 22:48:24 +02:00
|
|
|
bool m_moveLineUndoHack = false;
|
2014-07-23 19:10:38 +02:00
|
|
|
|
|
|
|
|
QTextCursor m_findScopeStart;
|
|
|
|
|
QTextCursor m_findScopeEnd;
|
2017-01-14 22:48:24 +02:00
|
|
|
int m_findScopeVerticalBlockSelectionFirstColumn = -1;
|
|
|
|
|
int m_findScopeVerticalBlockSelectionLastColumn = -1;
|
2014-07-23 19:10:38 +02:00
|
|
|
|
|
|
|
|
QTextCursor m_selectBlockAnchor;
|
|
|
|
|
|
2015-02-03 23:46:35 +02:00
|
|
|
TextBlockSelection m_blockSelection;
|
2014-07-23 19:10:38 +02:00
|
|
|
|
|
|
|
|
void moveCursorVisible(bool ensureVisible = true);
|
|
|
|
|
|
|
|
|
|
int visualIndent(const QTextBlock &block) const;
|
2014-09-26 11:37:54 +02:00
|
|
|
TextEditorPrivateHighlightBlocks m_highlightBlocksInfo;
|
2014-07-23 19:10:38 +02:00
|
|
|
QTimer m_highlightBlocksTimer;
|
|
|
|
|
|
2014-08-20 15:31:39 +02:00
|
|
|
CodeAssistant m_codeAssistant;
|
2017-01-14 22:48:24 +02:00
|
|
|
bool m_assistRelevantContentAdded = false;
|
2017-06-29 16:35:48 +02:00
|
|
|
|
2014-09-30 13:08:05 +02:00
|
|
|
QList<BaseHoverHandler *> m_hoverHandlers; // Not owned
|
2017-06-26 11:15:19 +02:00
|
|
|
HoverHandlerRunner m_hoverHandlerRunner;
|
2014-07-23 19:10:38 +02:00
|
|
|
|
2017-05-23 14:34:22 +02:00
|
|
|
QPointer<QSequentialAnimationGroup> m_navigationAnimation;
|
|
|
|
|
|
2016-04-28 12:51:34 +02:00
|
|
|
QPointer<TextEditorAnimator> m_bracketsAnimator;
|
2016-06-01 10:19:59 +02:00
|
|
|
|
|
|
|
|
// Animation and highlighting of auto completed text
|
2016-04-28 12:51:34 +02:00
|
|
|
QPointer<TextEditorAnimator> m_autocompleteAnimator;
|
|
|
|
|
bool m_animateAutoComplete = true;
|
2016-06-01 10:19:59 +02:00
|
|
|
bool m_highlightAutoComplete = true;
|
2016-06-10 15:13:38 +02:00
|
|
|
bool m_skipAutoCompletedText = true;
|
2016-06-14 08:00:32 +02:00
|
|
|
bool m_removeAutoCompletedText = true;
|
2016-06-01 10:19:59 +02:00
|
|
|
bool m_keepAutoCompletionHighlight = false;
|
2017-06-26 08:17:29 +02:00
|
|
|
QList<QTextCursor> m_autoCompleteHighlightPos;
|
|
|
|
|
void updateAutoCompleteHighlight();
|
2016-06-01 10:19:59 +02:00
|
|
|
|
2017-01-14 22:48:24 +02:00
|
|
|
int m_cursorBlockNumber = -1;
|
|
|
|
|
int m_blockCount = 0;
|
2014-07-23 19:10:38 +02:00
|
|
|
|
|
|
|
|
QPoint m_markDragStart;
|
2017-01-14 22:48:24 +02:00
|
|
|
bool m_markDragging = false;
|
2014-07-23 19:10:38 +02:00
|
|
|
|
2015-02-03 23:46:35 +02:00
|
|
|
QScopedPointer<ClipboardAssistProvider> m_clipboardAssistProvider;
|
2014-07-30 10:04:01 +02:00
|
|
|
|
2017-01-14 22:48:24 +02:00
|
|
|
bool m_isMissingSyntaxDefinition = false;
|
2014-08-19 12:12:45 +02:00
|
|
|
|
|
|
|
|
QScopedPointer<AutoCompleter> m_autoCompleter;
|
2014-09-01 16:16:44 +02:00
|
|
|
CommentDefinition m_commentDefinition;
|
2015-06-04 10:43:34 +02:00
|
|
|
|
2017-01-14 22:48:24 +02:00
|
|
|
QFutureWatcher<FileSearchResultList> *m_searchWatcher = nullptr;
|
2015-06-04 10:43:34 +02:00
|
|
|
QVector<SearchResult> m_searchResults;
|
|
|
|
|
QTimer m_scrollBarUpdateTimer;
|
2017-01-14 22:48:24 +02:00
|
|
|
HighlightScrollBar *m_highlightScrollBar = nullptr;
|
|
|
|
|
bool m_scrollBarUpdateScheduled = false;
|
2014-07-23 19:10:38 +02:00
|
|
|
};
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
TextEditorWidgetPrivate::TextEditorWidgetPrivate(TextEditorWidget *parent)
|
2014-08-01 16:20:25 +02:00
|
|
|
: q(parent),
|
|
|
|
|
m_marksVisible(false),
|
|
|
|
|
m_codeFoldingVisible(false),
|
|
|
|
|
m_codeFoldingSupported(false),
|
|
|
|
|
m_revisionsVisible(false),
|
|
|
|
|
m_lineNumbersVisible(true),
|
|
|
|
|
m_highlightCurrentLine(true),
|
|
|
|
|
m_requestMarkEnabled(true),
|
|
|
|
|
m_lineSeparatorsAllowed(false),
|
|
|
|
|
m_maybeFakeTooltipEvent(false),
|
2017-06-26 11:15:19 +02:00
|
|
|
m_hoverHandlerRunner(parent, m_hoverHandlers),
|
2015-02-03 23:46:35 +02:00
|
|
|
m_clipboardAssistProvider(new ClipboardAssistProvider),
|
2017-01-14 22:48:24 +02:00
|
|
|
m_autoCompleter(new AutoCompleter)
|
2014-08-01 16:20:25 +02:00
|
|
|
{
|
|
|
|
|
Aggregation::Aggregate *aggregate = new Aggregation::Aggregate;
|
|
|
|
|
BaseTextFind *baseTextFind = new BaseTextFind(q);
|
2014-08-28 17:32:34 +02:00
|
|
|
connect(baseTextFind, &BaseTextFind::highlightAllRequested,
|
2014-09-26 11:37:54 +02:00
|
|
|
this, &TextEditorWidgetPrivate::highlightSearchResultsSlot);
|
2014-08-01 16:20:25 +02:00
|
|
|
connect(baseTextFind, &BaseTextFind::findScopeChanged,
|
2014-09-26 11:37:54 +02:00
|
|
|
this, &TextEditorWidgetPrivate::setFindScope);
|
2014-08-01 16:20:25 +02:00
|
|
|
aggregate->add(baseTextFind);
|
|
|
|
|
aggregate->add(q);
|
2014-08-01 16:41:18 +02:00
|
|
|
|
2014-08-20 23:04:08 +02:00
|
|
|
m_extraArea = new TextEditExtraArea(q);
|
|
|
|
|
m_extraArea->setMouseTracking(true);
|
|
|
|
|
|
2014-08-01 16:41:18 +02:00
|
|
|
m_stretchWidget = new QWidget;
|
|
|
|
|
m_stretchWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
|
|
|
|
|
m_toolBar = new QToolBar;
|
|
|
|
|
m_toolBar->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
|
|
|
|
|
m_toolBar->addWidget(m_stretchWidget);
|
|
|
|
|
|
2014-08-19 14:23:03 +02:00
|
|
|
m_cursorPositionLabel = new LineColumnLabel;
|
2014-08-01 16:41:18 +02:00
|
|
|
const int spacing = q->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing) / 2;
|
|
|
|
|
m_cursorPositionLabel->setContentsMargins(spacing, 0, spacing, 0);
|
|
|
|
|
|
2014-08-19 14:23:03 +02:00
|
|
|
m_fileEncodingLabel = new LineColumnLabel;
|
2014-08-01 16:41:18 +02:00
|
|
|
m_fileEncodingLabel->setContentsMargins(spacing, 0, spacing, 0);
|
|
|
|
|
|
|
|
|
|
m_cursorPositionLabelAction = m_toolBar->addWidget(m_cursorPositionLabel);
|
|
|
|
|
m_fileEncodingLabelAction = m_toolBar->addWidget(m_fileEncodingLabel);
|
2015-06-23 15:56:45 +02:00
|
|
|
m_extraSelections.reserve(NExtraSelectionKinds);
|
2014-08-01 16:20:25 +02:00
|
|
|
}
|
2014-08-01 16:41:18 +02:00
|
|
|
|
2015-09-29 15:15:03 +02:00
|
|
|
TextEditorWidgetPrivate::~TextEditorWidgetPrivate()
|
|
|
|
|
{
|
2017-10-06 14:29:13 +02:00
|
|
|
QObject::disconnect(m_document.data(), &TextDocument::markRemoved,
|
|
|
|
|
this, &TextEditorWidgetPrivate::markRemoved);
|
2015-09-29 15:15:03 +02:00
|
|
|
q->disconnect(this);
|
|
|
|
|
delete m_toolBar;
|
|
|
|
|
}
|
|
|
|
|
|
2009-01-20 11:52:04 +01:00
|
|
|
} // namespace Internal
|
2013-05-24 14:23:07 +02:00
|
|
|
|
|
|
|
|
using namespace Internal;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2014-08-22 19:14:48 +02:00
|
|
|
/*!
|
|
|
|
|
* Test if syntax highlighter is available (or unneeded) for \a widget.
|
|
|
|
|
* If not found, show a warning with a link to the relevant settings page.
|
|
|
|
|
*/
|
2014-09-26 11:37:54 +02:00
|
|
|
static void updateEditorInfoBar(TextEditorWidget *widget)
|
2014-08-22 19:14:48 +02:00
|
|
|
{
|
|
|
|
|
Id infoSyntaxDefinition(Constants::INFO_SYNTAX_DEFINITION);
|
|
|
|
|
InfoBar *infoBar = widget->textDocument()->infoBar();
|
|
|
|
|
if (!widget->isMissingSyntaxDefinition()) {
|
|
|
|
|
infoBar->removeInfo(infoSyntaxDefinition);
|
|
|
|
|
} else if (infoBar->canInfoBeAdded(infoSyntaxDefinition)) {
|
|
|
|
|
InfoBarEntry info(infoSyntaxDefinition,
|
|
|
|
|
BaseTextEditor::tr("A highlight definition was not found for this file. "
|
|
|
|
|
"Would you like to try to find one?"),
|
|
|
|
|
InfoBarEntry::GlobalSuppressionEnabled);
|
2014-09-03 10:34:52 +02:00
|
|
|
info.setCustomButtonInfo(BaseTextEditor::tr("Show Highlighter Options..."), [widget]() {
|
2015-02-23 11:07:38 +01:00
|
|
|
ICore::showOptionsDialog(Constants::TEXT_EDITOR_HIGHLIGHTER_SETTINGS, widget);
|
2014-09-03 10:34:52 +02:00
|
|
|
});
|
|
|
|
|
|
2014-08-22 19:14:48 +02:00
|
|
|
infoBar->addInfo(info);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
QString TextEditorWidget::plainTextFromSelection(const QTextCursor &cursor) const
|
2009-09-30 17:48:57 +02:00
|
|
|
{
|
2013-01-20 23:09:09 +01:00
|
|
|
// Copy the selected text as plain text
|
|
|
|
|
QString text = cursor.selectedText();
|
|
|
|
|
return convertToPlainText(text);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
QString TextEditorWidget::convertToPlainText(const QString &txt)
|
2013-01-20 23:09:09 +01:00
|
|
|
{
|
|
|
|
|
QString ret = txt;
|
|
|
|
|
QChar *uc = ret.data();
|
|
|
|
|
QChar *e = uc + ret.size();
|
2009-09-30 17:48:57 +02:00
|
|
|
|
|
|
|
|
for (; uc != e; ++uc) {
|
|
|
|
|
switch (uc->unicode()) {
|
|
|
|
|
case 0xfdd0: // QTextBeginningOfFrame
|
|
|
|
|
case 0xfdd1: // QTextEndOfFrame
|
|
|
|
|
case QChar::ParagraphSeparator:
|
|
|
|
|
case QChar::LineSeparator:
|
|
|
|
|
*uc = QLatin1Char('\n');
|
|
|
|
|
break;
|
|
|
|
|
case QChar::Nbsp:
|
|
|
|
|
*uc = QLatin1Char(' ');
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-01-20 23:09:09 +01:00
|
|
|
return ret;
|
2009-09-30 17:48:57 +02:00
|
|
|
}
|
|
|
|
|
|
2013-05-21 23:37:47 +03:00
|
|
|
static const char kTextBlockMimeType[] = "application/vnd.qtcreator.blocktext";
|
2011-12-13 12:15:30 +01:00
|
|
|
|
2015-06-23 15:56:45 +02:00
|
|
|
Id TextEditorWidget::SnippetPlaceholderSelection("TextEdit.SnippetPlaceHolderSelection");
|
|
|
|
|
Id TextEditorWidget::CurrentLineSelection("TextEdit.CurrentLineSelection");
|
|
|
|
|
Id TextEditorWidget::ParenthesesMatchingSelection("TextEdit.ParenthesesMatchingSelection");
|
2016-06-01 10:19:59 +02:00
|
|
|
Id TextEditorWidget::AutoCompleteSelection("TextEdit.AutoCompleteSelection");
|
2015-06-23 15:56:45 +02:00
|
|
|
Id TextEditorWidget::CodeWarningsSelection("TextEdit.CodeWarningsSelection");
|
|
|
|
|
Id TextEditorWidget::CodeSemanticsSelection("TextEdit.CodeSemanticsSelection");
|
|
|
|
|
Id TextEditorWidget::UndefinedSymbolSelection("TextEdit.UndefinedSymbolSelection");
|
|
|
|
|
Id TextEditorWidget::UnusedSymbolSelection("TextEdit.UnusedSymbolSelection");
|
|
|
|
|
Id TextEditorWidget::OtherSelection("TextEdit.OtherSelection");
|
|
|
|
|
Id TextEditorWidget::ObjCSelection("TextEdit.ObjCSelection");
|
|
|
|
|
Id TextEditorWidget::DebuggerExceptionSelection("TextEdit.DebuggerExceptionSelection");
|
|
|
|
|
Id TextEditorWidget::FakeVimSelection("TextEdit.FakeVimSelection");
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
TextEditorWidget::TextEditorWidget(QWidget *parent)
|
2014-01-09 14:47:00 +01:00
|
|
|
: QPlainTextEdit(parent)
|
|
|
|
|
{
|
2014-08-20 23:04:08 +02:00
|
|
|
// "Needed", as the creation below triggers ChildEvents that are
|
|
|
|
|
// passed to this object's event() which uses 'd'.
|
|
|
|
|
d = 0;
|
2014-09-26 11:37:54 +02:00
|
|
|
d = new TextEditorWidgetPrivate(this);
|
2014-01-09 14:47:00 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setTextDocument(const QSharedPointer<TextDocument> &doc)
|
2014-01-09 18:04:45 +01:00
|
|
|
{
|
2014-08-18 17:49:04 +02:00
|
|
|
d->ctor(doc);
|
2014-01-09 18:04:45 +01:00
|
|
|
}
|
|
|
|
|
|
2015-06-04 10:43:34 +02:00
|
|
|
void TextEditorWidgetPrivate::setupScrollBar()
|
|
|
|
|
{
|
|
|
|
|
if (m_displaySettings.m_scrollBarHighlights) {
|
|
|
|
|
if (m_highlightScrollBar)
|
|
|
|
|
return;
|
|
|
|
|
m_highlightScrollBar = new HighlightScrollBar(Qt::Vertical, q);
|
|
|
|
|
q->setVerticalScrollBar(m_highlightScrollBar);
|
|
|
|
|
highlightSearchResultsInScrollBar();
|
|
|
|
|
scheduleUpdateHighlightScrollBar();
|
|
|
|
|
} else if (m_highlightScrollBar) {
|
|
|
|
|
q->setVerticalScrollBar(new QScrollBar(Qt::Vertical, q));
|
|
|
|
|
m_highlightScrollBar = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::ctor(const QSharedPointer<TextDocument> &doc)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
q->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
m_overlay = new TextEditorOverlay(q);
|
|
|
|
|
m_snippetOverlay = new TextEditorOverlay(q);
|
|
|
|
|
m_searchResultOverlay = new TextEditorOverlay(q);
|
|
|
|
|
m_refactorOverlay = new RefactorOverlay(q);
|
2009-11-25 15:55:45 +01:00
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
m_document = doc;
|
|
|
|
|
setupDocumentSignals();
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
// from RESEARCH
|
|
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
q->setLayoutDirection(Qt::LeftToRight);
|
|
|
|
|
q->viewport()->setMouseTracking(true);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
extraAreaSelectionAnchorBlockNumber = -1;
|
|
|
|
|
extraAreaToggleMarkBlockNumber = -1;
|
|
|
|
|
extraAreaHighlightFoldedBlockNumber = -1;
|
|
|
|
|
visibleFoldedBlockNumber = -1;
|
|
|
|
|
suggestedVisibleFoldedBlockNumber = -1;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2014-08-20 15:31:39 +02:00
|
|
|
QObject::connect(&m_codeAssistant, &CodeAssistant::finished,
|
2014-09-26 11:37:54 +02:00
|
|
|
q, &TextEditorWidget::assistFinished);
|
2013-03-19 11:41:40 +01:00
|
|
|
|
2014-08-01 15:06:28 +02:00
|
|
|
QObject::connect(q, &QPlainTextEdit::blockCountChanged,
|
2014-09-26 11:37:54 +02:00
|
|
|
this, &TextEditorWidgetPrivate::slotUpdateExtraAreaWidth);
|
2014-08-01 15:06:28 +02:00
|
|
|
|
2014-08-27 16:59:03 +02:00
|
|
|
QObject::connect(q, &QPlainTextEdit::modificationChanged, m_extraArea,
|
2014-08-01 15:06:28 +02:00
|
|
|
static_cast<void (QWidget::*)()>(&QWidget::update));
|
|
|
|
|
|
|
|
|
|
QObject::connect(q, &QPlainTextEdit::cursorPositionChanged,
|
2014-09-26 11:37:54 +02:00
|
|
|
q, &TextEditorWidget::slotCursorPositionChanged);
|
2014-08-01 15:06:28 +02:00
|
|
|
|
2014-08-01 16:41:18 +02:00
|
|
|
QObject::connect(q, &QPlainTextEdit::cursorPositionChanged,
|
2014-09-26 11:37:54 +02:00
|
|
|
this, &TextEditorWidgetPrivate::updateCursorPosition);
|
2014-08-01 16:41:18 +02:00
|
|
|
|
2014-08-01 15:06:28 +02:00
|
|
|
QObject::connect(q, &QPlainTextEdit::updateRequest,
|
2014-09-26 11:37:54 +02:00
|
|
|
this, &TextEditorWidgetPrivate::slotUpdateRequest);
|
2014-08-01 15:06:28 +02:00
|
|
|
|
|
|
|
|
QObject::connect(q, &QPlainTextEdit::selectionChanged,
|
2014-09-26 11:37:54 +02:00
|
|
|
this, &TextEditorWidgetPrivate::slotSelectionChanged);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
// parentheses matcher
|
2014-07-25 16:23:50 +02:00
|
|
|
m_formatRange = true;
|
|
|
|
|
m_parenthesesMatchingTimer.setSingleShot(true);
|
2014-08-01 15:06:28 +02:00
|
|
|
QObject::connect(&m_parenthesesMatchingTimer, &QTimer::timeout,
|
2014-09-26 11:37:54 +02:00
|
|
|
this, &TextEditorWidgetPrivate::_q_matchParentheses);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
m_highlightBlocksTimer.setSingleShot(true);
|
2014-08-01 15:06:28 +02:00
|
|
|
QObject::connect(&m_highlightBlocksTimer, &QTimer::timeout,
|
2014-09-26 11:37:54 +02:00
|
|
|
this, &TextEditorWidgetPrivate::_q_highlightBlocks);
|
2009-04-23 17:28:53 +02:00
|
|
|
|
2015-06-04 10:43:34 +02:00
|
|
|
m_scrollBarUpdateTimer.setSingleShot(true);
|
|
|
|
|
QObject::connect(&m_scrollBarUpdateTimer, &QTimer::timeout,
|
|
|
|
|
this, &TextEditorWidgetPrivate::highlightSearchResultsInScrollBar);
|
|
|
|
|
|
2016-04-28 12:51:34 +02:00
|
|
|
m_bracketsAnimator = 0;
|
|
|
|
|
m_autocompleteAnimator = 0;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2014-08-01 15:06:28 +02:00
|
|
|
slotUpdateExtraAreaWidth();
|
2010-02-08 16:54:14 +01:00
|
|
|
updateHighlights();
|
2014-07-25 16:23:50 +02:00
|
|
|
q->setFrameStyle(QFrame::NoFrame);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
m_delayedUpdateTimer.setSingleShot(true);
|
2014-08-01 15:06:28 +02:00
|
|
|
QObject::connect(&m_delayedUpdateTimer, &QTimer::timeout, q->viewport(),
|
|
|
|
|
static_cast<void (QWidget::*)()>(&QWidget::update));
|
2009-12-01 14:47:10 +01:00
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
m_moveLineUndoHack = false;
|
2014-09-30 15:38:31 +02:00
|
|
|
|
|
|
|
|
updateCannotDecodeInfo();
|
|
|
|
|
|
2015-06-02 17:14:48 +02:00
|
|
|
QObject::connect(m_document.data(), &TextDocument::aboutToOpen,
|
|
|
|
|
q, &TextEditorWidget::aboutToOpen);
|
|
|
|
|
QObject::connect(m_document.data(), &TextDocument::openFinishedSuccessfully,
|
|
|
|
|
q, &TextEditorWidget::openFinishedSuccessfully);
|
2014-09-30 15:38:31 +02:00
|
|
|
connect(m_fileEncodingLabel, &LineColumnLabel::clicked,
|
|
|
|
|
q, &TextEditorWidget::selectEncoding);
|
|
|
|
|
connect(m_document->document(), &QTextDocument::modificationChanged,
|
|
|
|
|
q, &TextEditorWidget::updateTextCodecLabel);
|
|
|
|
|
q->updateTextCodecLabel();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
TextEditorWidget::~TextEditorWidget()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
delete d;
|
|
|
|
|
d = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::print(QPrinter *printer)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2013-05-24 14:23:07 +02:00
|
|
|
const bool oldFullPage = printer->fullPage();
|
2008-12-02 12:01:29 +01:00
|
|
|
printer->setFullPage(true);
|
|
|
|
|
QPrintDialog *dlg = new QPrintDialog(printer, this);
|
|
|
|
|
dlg->setWindowTitle(tr("Print Document"));
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
if (dlg->exec() == QDialog::Accepted)
|
2008-12-02 12:01:29 +01:00
|
|
|
d->print(printer);
|
|
|
|
|
printer->setFullPage(oldFullPage);
|
|
|
|
|
delete dlg;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-20 15:10:26 +02:00
|
|
|
static int foldBoxWidth(const QFontMetrics &fm)
|
2009-06-05 16:16:03 +02:00
|
|
|
{
|
|
|
|
|
const int lineSpacing = fm.lineSpacing();
|
2013-05-24 14:23:07 +02:00
|
|
|
return lineSpacing + lineSpacing % 2 + 1;
|
2009-06-05 16:16:03 +02:00
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
static void printPage(int index, QPainter *painter, const QTextDocument *doc,
|
|
|
|
|
const QRectF &body, const QRectF &titleBox,
|
|
|
|
|
const QString &title)
|
|
|
|
|
{
|
|
|
|
|
painter->save();
|
|
|
|
|
|
|
|
|
|
painter->translate(body.left(), body.top() - (index - 1) * body.height());
|
2016-05-10 13:38:30 +03:00
|
|
|
const QRectF view(0, (index - 1) * body.height(), body.width(), body.height());
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
QAbstractTextDocumentLayout *layout = doc->documentLayout();
|
|
|
|
|
QAbstractTextDocumentLayout::PaintContext ctx;
|
|
|
|
|
|
|
|
|
|
painter->setFont(QFont(doc->defaultFont()));
|
2016-05-10 13:38:30 +03:00
|
|
|
const QRectF box = titleBox.translated(0, view.top());
|
|
|
|
|
const int dpix = painter->device()->logicalDpiX();
|
|
|
|
|
const int dpiy = painter->device()->logicalDpiY();
|
|
|
|
|
const int mx = 5 * dpix / 72.0;
|
|
|
|
|
const int my = 2 * dpiy / 72.0;
|
2008-12-02 12:01:29 +01:00
|
|
|
painter->fillRect(box.adjusted(-mx, -my, mx, my), QColor(210, 210, 210));
|
|
|
|
|
if (!title.isEmpty())
|
|
|
|
|
painter->drawText(box, Qt::AlignCenter, title);
|
|
|
|
|
const QString pageString = QString::number(index);
|
|
|
|
|
painter->drawText(box, Qt::AlignRight, pageString);
|
|
|
|
|
|
|
|
|
|
painter->setClipRect(view);
|
|
|
|
|
ctx.clip = view;
|
|
|
|
|
// don't use the system palette text as default text color, on HP/UX
|
|
|
|
|
// for example that's white, and white text on white paper doesn't
|
|
|
|
|
// look that nice
|
|
|
|
|
ctx.palette.setColor(QPalette::Text, Qt::black);
|
|
|
|
|
|
|
|
|
|
layout->draw(painter, ctx);
|
|
|
|
|
|
|
|
|
|
painter->restore();
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::print(QPrinter *printer)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
QTextDocument *doc = q->document();
|
|
|
|
|
|
2013-07-04 22:25:15 +02:00
|
|
|
QString title = m_document->displayName();
|
2014-05-12 07:01:59 +02:00
|
|
|
if (!title.isEmpty())
|
2008-12-02 12:01:29 +01:00
|
|
|
printer->setDocName(title);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QPainter p(printer);
|
|
|
|
|
|
|
|
|
|
// Check that there is a valid device to print to.
|
|
|
|
|
if (!p.isActive())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
doc = doc->clone(doc);
|
|
|
|
|
|
|
|
|
|
QTextOption opt = doc->defaultTextOption();
|
|
|
|
|
opt.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
|
|
|
|
|
doc->setDefaultTextOption(opt);
|
|
|
|
|
|
|
|
|
|
(void)doc->documentLayout(); // make sure that there is a layout
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QColor background = q->palette().color(QPalette::Base);
|
|
|
|
|
bool backgroundIsDark = background.value() < 128;
|
|
|
|
|
|
|
|
|
|
for (QTextBlock srcBlock = q->document()->firstBlock(), dstBlock = doc->firstBlock();
|
|
|
|
|
srcBlock.isValid() && dstBlock.isValid();
|
|
|
|
|
srcBlock = srcBlock.next(), dstBlock = dstBlock.next()) {
|
|
|
|
|
|
|
|
|
|
|
2016-08-03 23:09:08 +03:00
|
|
|
QVector<QTextLayout::FormatRange> formatList = srcBlock.layout()->formats();
|
2008-12-02 12:01:29 +01:00
|
|
|
if (backgroundIsDark) {
|
|
|
|
|
// adjust syntax highlighting colors for better contrast
|
2013-05-24 14:23:07 +02:00
|
|
|
for (int i = formatList.count() - 1; i >= 0; --i) {
|
2008-12-02 12:01:29 +01:00
|
|
|
QTextCharFormat &format = formatList[i].format;
|
|
|
|
|
if (format.background().color() == background) {
|
|
|
|
|
QBrush brush = format.foreground();
|
|
|
|
|
QColor color = brush.color();
|
|
|
|
|
int h,s,v,a;
|
|
|
|
|
color.getHsv(&h, &s, &v, &a);
|
|
|
|
|
color.setHsv(h, s, qMin(128, v), a);
|
|
|
|
|
brush.setColor(color);
|
|
|
|
|
format.setForeground(brush);
|
|
|
|
|
}
|
|
|
|
|
format.setBackground(Qt::white);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-08-03 23:09:08 +03:00
|
|
|
dstBlock.layout()->setFormats(formatList);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QAbstractTextDocumentLayout *layout = doc->documentLayout();
|
|
|
|
|
layout->setPaintDevice(p.device());
|
|
|
|
|
|
|
|
|
|
int dpiy = p.device()->logicalDpiY();
|
|
|
|
|
int margin = (int) ((2/2.54)*dpiy); // 2 cm margins
|
|
|
|
|
|
|
|
|
|
QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
|
|
|
|
|
fmt.setMargin(margin);
|
|
|
|
|
doc->rootFrame()->setFrameFormat(fmt);
|
|
|
|
|
|
|
|
|
|
QRectF pageRect(printer->pageRect());
|
|
|
|
|
QRectF body = QRectF(0, 0, pageRect.width(), pageRect.height());
|
|
|
|
|
QFontMetrics fontMetrics(doc->defaultFont(), p.device());
|
|
|
|
|
|
|
|
|
|
QRectF titleBox(margin,
|
|
|
|
|
body.top() + margin
|
|
|
|
|
- fontMetrics.height()
|
|
|
|
|
- 6 * dpiy / 72.0,
|
|
|
|
|
body.width() - 2*margin,
|
|
|
|
|
fontMetrics.height());
|
|
|
|
|
doc->setPageSize(body.size());
|
|
|
|
|
|
|
|
|
|
int docCopies;
|
|
|
|
|
int pageCopies;
|
2013-05-24 14:23:07 +02:00
|
|
|
if (printer->collateCopies() == true) {
|
2008-12-02 12:01:29 +01:00
|
|
|
docCopies = 1;
|
|
|
|
|
pageCopies = printer->numCopies();
|
|
|
|
|
} else {
|
|
|
|
|
docCopies = printer->numCopies();
|
|
|
|
|
pageCopies = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int fromPage = printer->fromPage();
|
|
|
|
|
int toPage = printer->toPage();
|
|
|
|
|
bool ascending = true;
|
|
|
|
|
|
|
|
|
|
if (fromPage == 0 && toPage == 0) {
|
|
|
|
|
fromPage = 1;
|
|
|
|
|
toPage = doc->pageCount();
|
|
|
|
|
}
|
|
|
|
|
// paranoia check
|
|
|
|
|
fromPage = qMax(1, fromPage);
|
|
|
|
|
toPage = qMin(doc->pageCount(), toPage);
|
|
|
|
|
|
|
|
|
|
if (printer->pageOrder() == QPrinter::LastPageFirst) {
|
|
|
|
|
int tmp = fromPage;
|
|
|
|
|
fromPage = toPage;
|
|
|
|
|
toPage = tmp;
|
|
|
|
|
ascending = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < docCopies; ++i) {
|
|
|
|
|
|
|
|
|
|
int page = fromPage;
|
|
|
|
|
while (true) {
|
|
|
|
|
for (int j = 0; j < pageCopies; ++j) {
|
|
|
|
|
if (printer->printerState() == QPrinter::Aborted
|
|
|
|
|
|| printer->printerState() == QPrinter::Error)
|
|
|
|
|
goto UserCanceled;
|
|
|
|
|
printPage(page, &p, doc, body, titleBox, title);
|
|
|
|
|
if (j < pageCopies - 1)
|
|
|
|
|
printer->newPage();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (page == toPage)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
if (ascending)
|
|
|
|
|
++page;
|
|
|
|
|
else
|
|
|
|
|
--page;
|
|
|
|
|
|
|
|
|
|
printer->newPage();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ( i < docCopies - 1)
|
|
|
|
|
printer->newPage();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UserCanceled:
|
|
|
|
|
delete doc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
int TextEditorWidgetPrivate::visualIndent(const QTextBlock &block) const
|
2009-04-23 17:28:53 +02:00
|
|
|
{
|
|
|
|
|
if (!block.isValid())
|
|
|
|
|
return 0;
|
|
|
|
|
const QTextDocument *document = block.document();
|
|
|
|
|
int i = 0;
|
|
|
|
|
while (i < block.length()) {
|
|
|
|
|
if (!document->characterAt(block.position() + i).isSpace()) {
|
|
|
|
|
QTextCursor cursor(block);
|
|
|
|
|
cursor.setPosition(block.position() + i);
|
|
|
|
|
return q->cursorRect(cursor).x();
|
|
|
|
|
}
|
|
|
|
|
++i;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2017-06-26 08:17:29 +02:00
|
|
|
void TextEditorWidgetPrivate::updateAutoCompleteHighlight()
|
|
|
|
|
{
|
|
|
|
|
const QTextCharFormat &matchFormat
|
2017-07-03 09:22:08 +02:00
|
|
|
= q->textDocument()->fontSettings().toTextCharFormat(C_AUTOCOMPLETE);
|
2017-06-26 08:17:29 +02:00
|
|
|
|
|
|
|
|
QList<QTextEdit::ExtraSelection> extraSelections;
|
|
|
|
|
for (QTextCursor cursor : Utils::asConst(m_autoCompleteHighlightPos)) {
|
|
|
|
|
QTextEdit::ExtraSelection sel;
|
|
|
|
|
sel.cursor = cursor;
|
|
|
|
|
sel.format.setBackground(matchFormat.background());
|
|
|
|
|
extraSelections.append(sel);
|
|
|
|
|
}
|
|
|
|
|
q->setExtraSelections(TextEditorWidget::AutoCompleteSelection, extraSelections);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::selectEncoding()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-09-22 18:43:31 +02:00
|
|
|
TextDocument *doc = d->m_document.data();
|
2008-12-02 12:01:29 +01:00
|
|
|
CodecSelector codecSelector(this, doc);
|
|
|
|
|
|
|
|
|
|
switch (codecSelector.exec()) {
|
2011-04-04 15:24:13 +02:00
|
|
|
case CodecSelector::Reload: {
|
|
|
|
|
QString errorString;
|
|
|
|
|
if (!doc->reload(&errorString, codecSelector.selectedCodec())) {
|
|
|
|
|
QMessageBox::critical(this, tr("File Error"), errorString);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break; }
|
2008-12-02 12:01:29 +01:00
|
|
|
case CodecSelector::Save:
|
|
|
|
|
doc->setCodec(codecSelector.selectedCodec());
|
2014-08-19 14:23:03 +02:00
|
|
|
EditorManager::saveDocument(textDocument());
|
2013-04-18 10:06:13 +02:00
|
|
|
updateTextCodecLabel();
|
2008-12-02 12:01:29 +01:00
|
|
|
break;
|
|
|
|
|
case CodecSelector::Cancel:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::updateTextCodecLabel()
|
2013-04-18 10:06:13 +02:00
|
|
|
{
|
2014-08-01 16:41:18 +02:00
|
|
|
QString text = QString::fromLatin1(d->m_document->codec()->name());
|
|
|
|
|
d->m_fileEncodingLabel->setText(text, text);
|
2013-04-18 10:06:13 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
QString TextEditorWidget::msgTextTooLarge(quint64 size)
|
2010-07-12 14:15:38 +02:00
|
|
|
{
|
|
|
|
|
return tr("The text is too large to be displayed (%1 MB).").
|
|
|
|
|
arg(size >> 20);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::insertPlainText(const QString &text)
|
2014-06-16 14:20:36 +02:00
|
|
|
{
|
|
|
|
|
if (d->m_inBlockSelectionMode)
|
|
|
|
|
d->insertIntoBlockSelection(text);
|
|
|
|
|
else
|
|
|
|
|
QPlainTextEdit::insertPlainText(text);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
QString TextEditorWidget::selectedText() const
|
2014-06-16 14:20:36 +02:00
|
|
|
{
|
|
|
|
|
if (d->m_inBlockSelectionMode)
|
|
|
|
|
return d->copyBlockSelection();
|
|
|
|
|
else
|
|
|
|
|
return textCursor().selectedText();
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::updateCannotDecodeInfo()
|
2011-05-06 12:48:44 +02:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
q->setReadOnly(m_document->hasDecodingError());
|
2014-08-19 14:23:03 +02:00
|
|
|
InfoBar *infoBar = m_document->infoBar();
|
|
|
|
|
Id selectEncodingId(Constants::SELECT_ENCODING);
|
2014-07-25 16:23:50 +02:00
|
|
|
if (m_document->hasDecodingError()) {
|
2013-11-14 09:41:02 +02:00
|
|
|
if (!infoBar->canInfoBeAdded(selectEncodingId))
|
|
|
|
|
return;
|
2014-08-19 14:23:03 +02:00
|
|
|
InfoBarEntry info(selectEncodingId,
|
2014-09-26 11:37:54 +02:00
|
|
|
TextEditorWidget::tr("<b>Error:</b> Could not decode \"%1\" with \"%2\"-encoding. Editing not possible.")
|
2014-07-25 16:23:50 +02:00
|
|
|
.arg(m_document->displayName()).arg(QString::fromLatin1(m_document->codec()->name())));
|
2014-09-26 11:37:54 +02:00
|
|
|
info.setCustomButtonInfo(TextEditorWidget::tr("Select Encoding"), [this]() { q->selectEncoding(); });
|
2013-11-14 09:41:02 +02:00
|
|
|
infoBar->addInfo(info);
|
2011-05-06 12:48:44 +02:00
|
|
|
} else {
|
2013-11-14 09:41:02 +02:00
|
|
|
infoBar->removeInfo(selectEncodingId);
|
2011-05-06 12:48:44 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-12 14:02:00 +02:00
|
|
|
// Skip over shebang to license header (Python, Perl, sh)
|
|
|
|
|
// '#!/bin/sh'
|
|
|
|
|
// ''
|
|
|
|
|
// '###############'
|
|
|
|
|
|
|
|
|
|
static QTextBlock skipShebang(const QTextBlock &block)
|
|
|
|
|
{
|
|
|
|
|
if (!block.isValid() || !block.text().startsWith("#!"))
|
|
|
|
|
return block;
|
|
|
|
|
const QTextBlock nextBlock1 = block.next();
|
|
|
|
|
if (!nextBlock1.isValid() || !nextBlock1.text().isEmpty())
|
|
|
|
|
return block;
|
|
|
|
|
const QTextBlock nextBlock2 = nextBlock1.next();
|
|
|
|
|
return nextBlock2.isValid() && nextBlock2.text().startsWith('#') ? nextBlock2 : block;
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-01 18:08:02 +01:00
|
|
|
/*
|
2017-09-12 14:02:00 +02:00
|
|
|
Collapses the first comment in a file, if there is only whitespace/shebang line
|
|
|
|
|
above
|
2009-12-01 18:08:02 +01:00
|
|
|
*/
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::foldLicenseHeader()
|
2009-12-01 18:08:02 +01:00
|
|
|
{
|
|
|
|
|
QTextDocument *doc = q->document();
|
2014-09-26 09:14:03 +02:00
|
|
|
TextDocumentLayout *documentLayout = qobject_cast<TextDocumentLayout*>(doc->documentLayout());
|
2009-12-01 18:08:02 +01:00
|
|
|
QTC_ASSERT(documentLayout, return);
|
2017-09-12 14:02:00 +02:00
|
|
|
QTextBlock block = skipShebang(doc->firstBlock());
|
2009-12-01 18:14:02 +01:00
|
|
|
while (block.isValid() && block.isVisible()) {
|
2009-12-01 18:08:02 +01:00
|
|
|
QString text = block.text();
|
2014-09-26 09:14:03 +02:00
|
|
|
if (TextDocumentLayout::canFold(block) && block.next().isVisible()) {
|
2017-09-12 14:02:00 +02:00
|
|
|
const QString trimmedText = text.trimmed();
|
|
|
|
|
if (trimmedText.startsWith("/*") || trimmedText.startsWith('#')) {
|
2014-09-26 09:14:03 +02:00
|
|
|
TextDocumentLayout::doFoldOrUnfold(block, false);
|
2010-05-20 15:10:26 +02:00
|
|
|
moveCursorVisible();
|
|
|
|
|
documentLayout->requestUpdate();
|
|
|
|
|
documentLayout->emitDocumentSizeChanged();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-08-08 13:08:20 +02:00
|
|
|
if (TabSettings::firstNonSpace(text) < text.size())
|
2009-12-01 18:08:02 +01:00
|
|
|
break;
|
|
|
|
|
block = block.next();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
TextDocument *TextEditorWidget::textDocument() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2013-01-09 13:31:15 +08:00
|
|
|
return d->m_document.data();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2015-06-02 17:14:48 +02:00
|
|
|
void TextEditorWidget::aboutToOpen(const QString &fileName, const QString &realFileName)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(fileName)
|
|
|
|
|
Q_UNUSED(realFileName)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TextEditorWidget::openFinishedSuccessfully()
|
|
|
|
|
{
|
|
|
|
|
moveCursor(QTextCursor::Start);
|
|
|
|
|
d->updateCannotDecodeInfo();
|
|
|
|
|
updateTextCodecLabel();
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
TextDocumentPtr TextEditorWidget::textDocumentPtr() const
|
2014-08-18 17:49:04 +02:00
|
|
|
{
|
|
|
|
|
return d->m_document;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-30 17:35:35 +02:00
|
|
|
TextEditorWidget *TextEditorWidget::currentTextEditorWidget()
|
|
|
|
|
{
|
|
|
|
|
BaseTextEditor *editor = qobject_cast<BaseTextEditor *>(EditorManager::currentEditor());
|
|
|
|
|
return editor ? editor->editorWidget() : 0;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::editorContentsChange(int position, int charsRemoved, int charsAdded)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2016-04-28 12:51:34 +02:00
|
|
|
if (m_bracketsAnimator)
|
|
|
|
|
m_bracketsAnimator->finish();
|
2009-09-23 12:50:02 +02:00
|
|
|
|
2014-08-01 15:06:28 +02:00
|
|
|
m_contentsChanged = true;
|
|
|
|
|
QTextDocument *doc = q->document();
|
2014-09-26 09:14:03 +02:00
|
|
|
TextDocumentLayout *documentLayout = static_cast<TextDocumentLayout*>(doc->documentLayout());
|
2014-03-07 11:29:35 +01:00
|
|
|
const QTextBlock posBlock = doc->findBlock(position);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
// Keep the line numbers and the block information for the text marks updated
|
|
|
|
|
if (charsRemoved != 0) {
|
2012-02-14 19:27:15 +01:00
|
|
|
documentLayout->updateMarksLineNumber();
|
2014-03-07 11:29:35 +01:00
|
|
|
documentLayout->updateMarksBlock(posBlock);
|
2008-12-02 12:01:29 +01:00
|
|
|
} else {
|
2010-03-29 14:20:12 +02:00
|
|
|
const QTextBlock nextBlock = doc->findBlock(position + charsAdded);
|
2008-12-02 12:01:29 +01:00
|
|
|
if (posBlock != nextBlock) {
|
2012-02-14 19:27:15 +01:00
|
|
|
documentLayout->updateMarksLineNumber();
|
|
|
|
|
documentLayout->updateMarksBlock(posBlock);
|
|
|
|
|
documentLayout->updateMarksBlock(nextBlock);
|
2008-12-02 12:01:29 +01:00
|
|
|
} else {
|
2012-02-14 19:27:15 +01:00
|
|
|
documentLayout->updateMarksBlock(posBlock);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
2010-03-29 14:20:12 +02:00
|
|
|
|
2014-08-01 15:06:28 +02:00
|
|
|
if (m_snippetOverlay->isVisible()) {
|
|
|
|
|
QTextCursor cursor = q->textCursor();
|
2010-09-24 13:02:13 +02:00
|
|
|
cursor.setPosition(position);
|
2014-08-01 15:06:28 +02:00
|
|
|
snippetCheckCursor(cursor);
|
2010-09-24 13:02:13 +02:00
|
|
|
}
|
|
|
|
|
|
2014-08-01 15:06:28 +02:00
|
|
|
if (charsAdded != 0 && q->document()->characterAt(position + charsAdded - 1).isPrint())
|
|
|
|
|
m_assistRelevantContentAdded = true;
|
2014-03-07 11:29:35 +01:00
|
|
|
|
|
|
|
|
int newBlockCount = doc->blockCount();
|
2014-08-01 15:06:28 +02:00
|
|
|
if (!q->hasFocus() && newBlockCount != m_blockCount) {
|
2014-03-07 11:29:35 +01:00
|
|
|
// lines were inserted or removed from outside, keep viewport on same part of text
|
2014-08-01 15:06:28 +02:00
|
|
|
if (q->firstVisibleBlock().blockNumber() > posBlock.blockNumber())
|
|
|
|
|
q->verticalScrollBar()->setValue(q->verticalScrollBar()->value() + newBlockCount - m_blockCount);
|
2017-08-18 23:20:21 +03:00
|
|
|
|
|
|
|
|
if (m_inBlockSelectionMode) {
|
|
|
|
|
disableBlockSelection(CursorUpdateClearSelection);
|
|
|
|
|
q->viewport()->update();
|
|
|
|
|
}
|
2014-03-07 11:29:35 +01:00
|
|
|
}
|
2014-08-01 15:06:28 +02:00
|
|
|
m_blockCount = newBlockCount;
|
2015-06-04 10:43:34 +02:00
|
|
|
m_scrollBarUpdateTimer.start(500);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::slotSelectionChanged()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-08-01 15:06:28 +02:00
|
|
|
if (!q->textCursor().hasSelection() && !m_selectBlockAnchor.isNull())
|
|
|
|
|
m_selectBlockAnchor = QTextCursor();
|
2009-11-03 14:29:49 +01:00
|
|
|
// Clear any link which might be showing when the selection changes
|
2014-08-01 15:06:28 +02:00
|
|
|
clearLink();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::gotoBlockStart()
|
2008-12-04 18:37:02 +01:00
|
|
|
{
|
|
|
|
|
QTextCursor cursor = textCursor();
|
2008-12-05 17:06:17 +01:00
|
|
|
if (TextBlockUserData::findPreviousOpenParenthesis(&cursor, false)) {
|
2008-12-04 18:37:02 +01:00
|
|
|
setTextCursor(cursor);
|
2014-08-01 15:06:28 +02:00
|
|
|
d->_q_matchParentheses();
|
2008-12-05 17:06:17 +01:00
|
|
|
}
|
2008-12-04 18:37:02 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::gotoBlockEnd()
|
2008-12-04 18:37:02 +01:00
|
|
|
{
|
|
|
|
|
QTextCursor cursor = textCursor();
|
2008-12-05 17:06:17 +01:00
|
|
|
if (TextBlockUserData::findNextClosingParenthesis(&cursor, false)) {
|
2008-12-04 18:37:02 +01:00
|
|
|
setTextCursor(cursor);
|
2014-08-01 15:06:28 +02:00
|
|
|
d->_q_matchParentheses();
|
2008-12-05 17:06:17 +01:00
|
|
|
}
|
2008-12-04 18:37:02 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::gotoBlockStartWithSelection()
|
2008-12-04 18:37:02 +01:00
|
|
|
{
|
|
|
|
|
QTextCursor cursor = textCursor();
|
2008-12-05 17:06:17 +01:00
|
|
|
if (TextBlockUserData::findPreviousOpenParenthesis(&cursor, true)) {
|
2008-12-04 18:37:02 +01:00
|
|
|
setTextCursor(cursor);
|
2014-08-01 15:06:28 +02:00
|
|
|
d->_q_matchParentheses();
|
2008-12-05 17:06:17 +01:00
|
|
|
}
|
2008-12-04 18:37:02 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::gotoBlockEndWithSelection()
|
2008-12-04 18:37:02 +01:00
|
|
|
{
|
|
|
|
|
QTextCursor cursor = textCursor();
|
2008-12-05 17:06:17 +01:00
|
|
|
if (TextBlockUserData::findNextClosingParenthesis(&cursor, true)) {
|
2008-12-04 18:37:02 +01:00
|
|
|
setTextCursor(cursor);
|
2014-08-01 15:06:28 +02:00
|
|
|
d->_q_matchParentheses();
|
2008-12-05 17:06:17 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::gotoLineStart()
|
2010-05-07 15:24:30 +02:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
d->handleHomeKey(false);
|
2010-05-07 15:24:30 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::gotoLineStartWithSelection()
|
2010-05-07 15:24:30 +02:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
d->handleHomeKey(true);
|
2010-05-07 15:24:30 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::gotoLineEnd()
|
2010-05-07 15:24:30 +02:00
|
|
|
{
|
|
|
|
|
moveCursor(QTextCursor::EndOfLine);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::gotoLineEndWithSelection()
|
2010-05-07 15:24:30 +02:00
|
|
|
{
|
|
|
|
|
moveCursor(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::gotoNextLine()
|
2010-05-07 15:24:30 +02:00
|
|
|
{
|
2010-08-23 11:05:49 +02:00
|
|
|
moveCursor(QTextCursor::Down);
|
2010-05-07 15:24:30 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::gotoNextLineWithSelection()
|
2010-05-07 15:24:30 +02:00
|
|
|
{
|
2010-08-23 11:05:49 +02:00
|
|
|
moveCursor(QTextCursor::Down, QTextCursor::KeepAnchor);
|
2010-05-07 15:24:30 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::gotoPreviousLine()
|
2010-05-07 15:24:30 +02:00
|
|
|
{
|
2010-08-23 11:05:49 +02:00
|
|
|
moveCursor(QTextCursor::Up);
|
2010-05-07 15:24:30 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::gotoPreviousLineWithSelection()
|
2010-05-07 15:24:30 +02:00
|
|
|
{
|
2010-08-23 11:05:49 +02:00
|
|
|
moveCursor(QTextCursor::Up, QTextCursor::KeepAnchor);
|
2010-05-07 15:24:30 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::gotoPreviousCharacter()
|
2010-05-07 15:24:30 +02:00
|
|
|
{
|
|
|
|
|
moveCursor(QTextCursor::PreviousCharacter);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::gotoPreviousCharacterWithSelection()
|
2010-05-07 15:24:30 +02:00
|
|
|
{
|
|
|
|
|
moveCursor(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::gotoNextCharacter()
|
2010-05-07 15:24:30 +02:00
|
|
|
{
|
|
|
|
|
moveCursor(QTextCursor::NextCharacter);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::gotoNextCharacterWithSelection()
|
2010-05-07 15:24:30 +02:00
|
|
|
{
|
|
|
|
|
moveCursor(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::gotoPreviousWord()
|
2010-05-07 15:24:30 +02:00
|
|
|
{
|
|
|
|
|
moveCursor(QTextCursor::PreviousWord);
|
2011-05-22 08:51:30 +02:00
|
|
|
setTextCursor(textCursor());
|
2010-05-07 15:24:30 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::gotoPreviousWordWithSelection()
|
2010-05-07 15:24:30 +02:00
|
|
|
{
|
|
|
|
|
moveCursor(QTextCursor::PreviousWord, QTextCursor::KeepAnchor);
|
2011-05-22 08:51:30 +02:00
|
|
|
setTextCursor(textCursor());
|
2010-05-07 15:24:30 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::gotoNextWord()
|
2010-05-07 15:24:30 +02:00
|
|
|
{
|
|
|
|
|
moveCursor(QTextCursor::NextWord);
|
2011-05-22 08:51:30 +02:00
|
|
|
setTextCursor(textCursor());
|
2010-05-07 15:24:30 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::gotoNextWordWithSelection()
|
2010-05-07 15:24:30 +02:00
|
|
|
{
|
|
|
|
|
moveCursor(QTextCursor::NextWord, QTextCursor::KeepAnchor);
|
2011-05-22 08:51:30 +02:00
|
|
|
setTextCursor(textCursor());
|
2010-05-07 15:24:30 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::gotoPreviousWordCamelCase()
|
2010-11-10 12:53:45 +01:00
|
|
|
{
|
|
|
|
|
QTextCursor c = textCursor();
|
2014-07-25 16:23:50 +02:00
|
|
|
d->camelCaseLeft(c, QTextCursor::MoveAnchor);
|
2010-11-10 12:53:45 +01:00
|
|
|
setTextCursor(c);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::gotoPreviousWordCamelCaseWithSelection()
|
2010-11-10 12:53:45 +01:00
|
|
|
{
|
|
|
|
|
QTextCursor c = textCursor();
|
2014-07-25 16:23:50 +02:00
|
|
|
d->camelCaseLeft(c, QTextCursor::KeepAnchor);
|
2010-11-10 12:53:45 +01:00
|
|
|
setTextCursor(c);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::gotoNextWordCamelCase()
|
2010-11-10 12:53:45 +01:00
|
|
|
{
|
|
|
|
|
QTextCursor c = textCursor();
|
2014-07-25 16:23:50 +02:00
|
|
|
d->camelCaseRight(c, QTextCursor::MoveAnchor);
|
2010-11-10 12:53:45 +01:00
|
|
|
setTextCursor(c);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::gotoNextWordCamelCaseWithSelection()
|
2010-11-10 12:53:45 +01:00
|
|
|
{
|
|
|
|
|
QTextCursor c = textCursor();
|
2014-07-25 16:23:50 +02:00
|
|
|
d->camelCaseRight(c, QTextCursor::KeepAnchor);
|
2010-11-10 12:53:45 +01:00
|
|
|
setTextCursor(c);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::selectBlockUp()
|
2008-12-04 19:25:20 +01:00
|
|
|
{
|
|
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
|
if (!cursor.hasSelection())
|
|
|
|
|
d->m_selectBlockAnchor = cursor;
|
|
|
|
|
else
|
|
|
|
|
cursor.setPosition(cursor.selectionStart());
|
|
|
|
|
|
|
|
|
|
if (!TextBlockUserData::findPreviousOpenParenthesis(&cursor, false))
|
2011-10-29 13:09:33 +08:00
|
|
|
return false;
|
2008-12-04 19:25:20 +01:00
|
|
|
if (!TextBlockUserData::findNextClosingParenthesis(&cursor, true))
|
2011-10-29 13:09:33 +08:00
|
|
|
return false;
|
|
|
|
|
|
2017-09-21 12:35:24 +02:00
|
|
|
setTextCursor(Text::flippedCursor(cursor));
|
2014-08-01 15:06:28 +02:00
|
|
|
d->_q_matchParentheses();
|
2011-10-29 13:09:33 +08:00
|
|
|
return true;
|
2008-12-04 19:25:20 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::selectBlockDown()
|
2008-12-04 19:25:20 +01:00
|
|
|
{
|
|
|
|
|
QTextCursor tc = textCursor();
|
|
|
|
|
QTextCursor cursor = d->m_selectBlockAnchor;
|
|
|
|
|
|
|
|
|
|
if (!tc.hasSelection() || cursor.isNull())
|
2011-10-29 13:09:33 +08:00
|
|
|
return false;
|
2008-12-04 19:25:20 +01:00
|
|
|
tc.setPosition(tc.selectionStart());
|
|
|
|
|
|
|
|
|
|
forever {
|
|
|
|
|
QTextCursor ahead = cursor;
|
|
|
|
|
if (!TextBlockUserData::findPreviousOpenParenthesis(&ahead, false))
|
|
|
|
|
break;
|
|
|
|
|
if (ahead.position() <= tc.position())
|
|
|
|
|
break;
|
|
|
|
|
cursor = ahead;
|
|
|
|
|
}
|
|
|
|
|
if ( cursor != d->m_selectBlockAnchor)
|
|
|
|
|
TextBlockUserData::findNextClosingParenthesis(&cursor, true);
|
|
|
|
|
|
2017-09-21 12:35:24 +02:00
|
|
|
setTextCursor(Text::flippedCursor(cursor));
|
2014-08-01 15:06:28 +02:00
|
|
|
d->_q_matchParentheses();
|
2011-10-29 13:09:33 +08:00
|
|
|
return true;
|
2008-12-04 19:25:20 +01:00
|
|
|
}
|
|
|
|
|
|
2016-08-02 08:17:35 +02:00
|
|
|
void TextEditorWidget::selectWordUnderCursor()
|
|
|
|
|
{
|
|
|
|
|
QTextCursor tc = textCursor();
|
|
|
|
|
if (tc.hasSelection())
|
|
|
|
|
return;
|
|
|
|
|
tc.select(QTextCursor::WordUnderCursor);
|
|
|
|
|
setTextCursor(tc);
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-01 17:15:35 +02:00
|
|
|
void TextEditorWidget::showContextMenu()
|
|
|
|
|
{
|
|
|
|
|
QTextCursor tc = textCursor();
|
|
|
|
|
const QPoint cursorPos = mapToGlobal(cursorRect(tc).bottomRight() + QPoint(1,1));
|
|
|
|
|
qGuiApp->postEvent(this, new QContextMenuEvent(QContextMenuEvent::Keyboard, cursorPos));
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::copyLineUp()
|
2009-05-15 23:17:11 +02:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
d->copyLineUpDown(true);
|
2009-05-15 23:17:11 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::copyLineDown()
|
2009-05-15 23:17:11 +02:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
d->copyLineUpDown(false);
|
2009-05-15 23:17:11 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-09 11:09:03 +01:00
|
|
|
// @todo: Potential reuse of some code around the following functions...
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::copyLineUpDown(bool up)
|
2009-05-15 23:17:11 +02:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
QTextCursor cursor = q->textCursor();
|
2009-05-15 23:17:11 +02:00
|
|
|
QTextCursor move = cursor;
|
|
|
|
|
move.beginEditBlock();
|
|
|
|
|
|
|
|
|
|
bool hasSelection = cursor.hasSelection();
|
|
|
|
|
|
|
|
|
|
if (hasSelection) {
|
|
|
|
|
move.setPosition(cursor.selectionStart());
|
|
|
|
|
move.movePosition(QTextCursor::StartOfBlock);
|
|
|
|
|
move.setPosition(cursor.selectionEnd(), QTextCursor::KeepAnchor);
|
2011-02-09 11:09:03 +01:00
|
|
|
move.movePosition(move.atBlockStart() ? QTextCursor::Left: QTextCursor::EndOfBlock,
|
|
|
|
|
QTextCursor::KeepAnchor);
|
2009-05-15 23:17:11 +02:00
|
|
|
} else {
|
|
|
|
|
move.movePosition(QTextCursor::StartOfBlock);
|
|
|
|
|
move.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString text = move.selectedText();
|
|
|
|
|
|
|
|
|
|
if (up) {
|
|
|
|
|
move.setPosition(cursor.selectionStart());
|
|
|
|
|
move.movePosition(QTextCursor::StartOfBlock);
|
|
|
|
|
move.insertBlock();
|
|
|
|
|
move.movePosition(QTextCursor::Left);
|
|
|
|
|
} else {
|
|
|
|
|
move.movePosition(QTextCursor::EndOfBlock);
|
|
|
|
|
if (move.atBlockStart()) {
|
|
|
|
|
move.movePosition(QTextCursor::NextBlock);
|
|
|
|
|
move.insertBlock();
|
|
|
|
|
move.movePosition(QTextCursor::Left);
|
|
|
|
|
} else {
|
|
|
|
|
move.insertBlock();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int start = move.position();
|
|
|
|
|
move.clearSelection();
|
|
|
|
|
move.insertText(text);
|
|
|
|
|
int end = move.position();
|
|
|
|
|
|
|
|
|
|
move.setPosition(start);
|
|
|
|
|
move.setPosition(end, QTextCursor::KeepAnchor);
|
|
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
m_document->autoIndent(move);
|
2009-05-15 23:17:11 +02:00
|
|
|
move.endEditBlock();
|
|
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
q->setTextCursor(move);
|
2009-05-15 23:17:11 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::joinLines()
|
2010-01-20 16:54:06 +01:00
|
|
|
{
|
2010-01-21 15:14:35 +01:00
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
|
QTextCursor start = cursor;
|
|
|
|
|
QTextCursor end = cursor;
|
2010-01-20 16:54:06 +01:00
|
|
|
|
2010-01-21 15:14:35 +01:00
|
|
|
start.setPosition(cursor.selectionStart());
|
|
|
|
|
end.setPosition(cursor.selectionEnd() - 1);
|
2010-01-20 17:51:05 +01:00
|
|
|
|
2010-01-21 15:14:35 +01:00
|
|
|
int lineCount = qMax(1, end.blockNumber() - start.blockNumber());
|
2010-01-20 16:54:07 +01:00
|
|
|
|
2010-01-21 15:14:35 +01:00
|
|
|
cursor.beginEditBlock();
|
|
|
|
|
cursor.setPosition(cursor.selectionStart());
|
|
|
|
|
while (lineCount--) {
|
|
|
|
|
cursor.movePosition(QTextCursor::NextBlock);
|
|
|
|
|
cursor.movePosition(QTextCursor::StartOfBlock);
|
|
|
|
|
cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
|
|
|
|
QString cutLine = cursor.selectedText();
|
2010-01-20 17:51:05 +01:00
|
|
|
|
2010-01-21 15:14:35 +01:00
|
|
|
// Collapse leading whitespaces to one or insert whitespace
|
2012-01-05 11:05:28 +01:00
|
|
|
cutLine.replace(QRegExp(QLatin1String("^\\s*")), QLatin1String(" "));
|
2010-01-21 15:14:35 +01:00
|
|
|
cursor.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
|
|
|
|
|
cursor.removeSelectedText();
|
|
|
|
|
|
|
|
|
|
cursor.movePosition(QTextCursor::PreviousBlock);
|
|
|
|
|
cursor.movePosition(QTextCursor::EndOfBlock);
|
|
|
|
|
|
|
|
|
|
cursor.insertText(cutLine);
|
|
|
|
|
}
|
|
|
|
|
cursor.endEditBlock();
|
|
|
|
|
|
|
|
|
|
setTextCursor(cursor);
|
2010-01-20 16:54:06 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::insertLineAbove()
|
2010-05-03 17:03:11 +02:00
|
|
|
{
|
|
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
|
cursor.beginEditBlock();
|
2012-11-22 11:40:06 +01:00
|
|
|
// If the cursor is at the beginning of the document,
|
|
|
|
|
// it should still insert a line above the current line.
|
|
|
|
|
cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
2010-05-03 17:03:11 +02:00
|
|
|
cursor.insertBlock();
|
2012-11-22 11:40:06 +01:00
|
|
|
cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor);
|
2014-01-15 17:09:52 +01:00
|
|
|
d->m_document->autoIndent(cursor);
|
2010-05-03 17:03:11 +02:00
|
|
|
cursor.endEditBlock();
|
|
|
|
|
setTextCursor(cursor);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::insertLineBelow()
|
2010-05-03 17:03:11 +02:00
|
|
|
{
|
2014-06-16 14:20:36 +02:00
|
|
|
if (d->m_inBlockSelectionMode)
|
2016-12-21 08:44:10 +01:00
|
|
|
d->disableBlockSelection(TextEditorWidgetPrivate::NoCursorUpdate);
|
2010-05-03 17:03:11 +02:00
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
|
cursor.beginEditBlock();
|
|
|
|
|
cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::MoveAnchor);
|
|
|
|
|
cursor.insertBlock();
|
2014-01-15 17:09:52 +01:00
|
|
|
d->m_document->autoIndent(cursor);
|
2010-05-03 17:03:11 +02:00
|
|
|
cursor.endEditBlock();
|
|
|
|
|
setTextCursor(cursor);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::moveLineUp()
|
2008-12-10 18:01:33 +01:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
d->moveLineUpDown(true);
|
2008-12-10 18:01:33 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::moveLineDown()
|
2008-12-10 18:01:33 +01:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
d->moveLineUpDown(false);
|
2008-12-10 18:01:33 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::uppercaseSelection()
|
2011-02-25 11:10:42 +01:00
|
|
|
{
|
2014-07-29 22:02:56 -07:00
|
|
|
d->transformSelection(&QString_toUpper);
|
2011-02-25 11:10:42 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::lowercaseSelection()
|
2011-02-25 11:10:42 +01:00
|
|
|
{
|
2014-07-29 22:02:56 -07:00
|
|
|
d->transformSelection(&QString_toLower);
|
2011-02-25 11:10:42 +01:00
|
|
|
}
|
|
|
|
|
|
2017-08-13 20:38:08 +03:00
|
|
|
void TextEditorWidget::sortSelectedLines()
|
|
|
|
|
{
|
|
|
|
|
d->transformSelectedLines([](QStringList &list) { list.sort(); });
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::indent()
|
2012-01-22 17:05:02 -05:00
|
|
|
{
|
2015-07-16 15:56:25 +02:00
|
|
|
int offset = 0;
|
|
|
|
|
doSetTextCursor(textDocument()->indent(textCursor(), d->m_inBlockSelectionMode,
|
|
|
|
|
d->m_blockSelection.positionColumn, &offset),
|
|
|
|
|
d->m_inBlockSelectionMode);
|
|
|
|
|
if (d->m_inBlockSelectionMode) {
|
|
|
|
|
d->m_blockSelection.anchorColumn += offset;
|
|
|
|
|
d->m_blockSelection.positionColumn += offset;
|
|
|
|
|
}
|
2012-01-22 17:05:02 -05:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::unindent()
|
2012-01-22 17:05:02 -05:00
|
|
|
{
|
2015-07-16 15:56:25 +02:00
|
|
|
int offset = 0;
|
|
|
|
|
doSetTextCursor(textDocument()->unindent(textCursor(), d->m_inBlockSelectionMode,
|
|
|
|
|
d->m_blockSelection.positionColumn, &offset),
|
|
|
|
|
d->m_inBlockSelectionMode);
|
|
|
|
|
if (d->m_inBlockSelectionMode) {
|
|
|
|
|
d->m_blockSelection.anchorColumn += offset;
|
|
|
|
|
d->m_blockSelection.positionColumn += offset;
|
|
|
|
|
}
|
2012-01-22 17:05:02 -05:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::undo()
|
2014-06-16 14:20:36 +02:00
|
|
|
{
|
|
|
|
|
if (d->m_inBlockSelectionMode)
|
2016-12-21 08:44:10 +01:00
|
|
|
d->disableBlockSelection(TextEditorWidgetPrivate::CursorUpdateClearSelection);
|
2014-06-16 14:20:36 +02:00
|
|
|
QPlainTextEdit::undo();
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::redo()
|
2014-06-16 14:20:36 +02:00
|
|
|
{
|
|
|
|
|
if (d->m_inBlockSelectionMode)
|
2016-12-21 08:44:10 +01:00
|
|
|
d->disableBlockSelection(TextEditorWidgetPrivate::CursorUpdateClearSelection);
|
2014-06-16 14:20:36 +02:00
|
|
|
QPlainTextEdit::redo();
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::openLinkUnderCursor()
|
2012-03-27 23:18:46 +04:00
|
|
|
{
|
2013-07-04 20:11:10 +02:00
|
|
|
const bool openInNextSplit = alwaysOpenLinksInNextSplit();
|
|
|
|
|
Link symbolLink = findLinkAt(textCursor(), true, openInNextSplit);
|
|
|
|
|
openLink(symbolLink, openInNextSplit);
|
2013-02-05 14:14:33 +01:00
|
|
|
}
|
2012-11-01 03:43:28 +01:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::openLinkUnderCursorInNextSplit()
|
2013-02-05 14:14:33 +01:00
|
|
|
{
|
2013-07-04 20:11:10 +02:00
|
|
|
const bool openInNextSplit = !alwaysOpenLinksInNextSplit();
|
|
|
|
|
Link symbolLink = findLinkAt(textCursor(), true, openInNextSplit);
|
|
|
|
|
openLink(symbolLink, openInNextSplit);
|
2012-03-27 23:18:46 +04:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::abortAssist()
|
2013-03-19 11:41:40 +01:00
|
|
|
{
|
2014-08-20 15:31:39 +02:00
|
|
|
d->m_codeAssistant.destroyContext();
|
2013-03-19 11:41:40 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::moveLineUpDown(bool up)
|
2008-12-10 18:01:33 +01:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
QTextCursor cursor = q->textCursor();
|
2008-12-10 18:01:33 +01:00
|
|
|
QTextCursor move = cursor;
|
2010-02-26 14:51:01 +01:00
|
|
|
|
2010-05-20 15:10:26 +02:00
|
|
|
move.setVisualNavigation(false); // this opens folded items instead of destroying them
|
2010-02-26 14:51:01 +01:00
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
if (m_moveLineUndoHack)
|
2009-04-23 19:24:57 +02:00
|
|
|
move.joinPreviousEditBlock();
|
|
|
|
|
else
|
|
|
|
|
move.beginEditBlock();
|
2008-12-10 18:01:33 +01:00
|
|
|
|
|
|
|
|
bool hasSelection = cursor.hasSelection();
|
|
|
|
|
|
2013-04-19 10:57:24 +04:00
|
|
|
if (hasSelection) {
|
2014-07-25 16:23:50 +02:00
|
|
|
if (m_inBlockSelectionMode)
|
2016-12-21 08:44:10 +01:00
|
|
|
disableBlockSelection(NoCursorUpdate);
|
2008-12-10 18:01:33 +01:00
|
|
|
move.setPosition(cursor.selectionStart());
|
|
|
|
|
move.movePosition(QTextCursor::StartOfBlock);
|
|
|
|
|
move.setPosition(cursor.selectionEnd(), QTextCursor::KeepAnchor);
|
2011-02-09 11:09:03 +01:00
|
|
|
move.movePosition(move.atBlockStart() ? QTextCursor::Left: QTextCursor::EndOfBlock,
|
|
|
|
|
QTextCursor::KeepAnchor);
|
2008-12-10 18:01:33 +01:00
|
|
|
} else {
|
|
|
|
|
move.movePosition(QTextCursor::StartOfBlock);
|
|
|
|
|
move.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
|
|
|
|
}
|
|
|
|
|
QString text = move.selectedText();
|
2010-09-07 14:34:40 +02:00
|
|
|
|
|
|
|
|
RefactorMarkers affectedMarkers;
|
|
|
|
|
RefactorMarkers nonAffectedMarkers;
|
|
|
|
|
QList<int> markerOffsets;
|
|
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
foreach (const RefactorMarker &marker, m_refactorOverlay->markers()) {
|
2010-09-07 14:34:40 +02:00
|
|
|
//test if marker is part of the selection to be moved
|
2011-02-09 11:09:03 +01:00
|
|
|
if ((move.selectionStart() <= marker.cursor.position())
|
|
|
|
|
&& (move.selectionEnd() >= marker.cursor.position())) {
|
2010-09-07 14:34:40 +02:00
|
|
|
affectedMarkers.append(marker);
|
|
|
|
|
//remember the offset of markers in text
|
|
|
|
|
int offset = marker.cursor.position() - move.selectionStart();
|
|
|
|
|
markerOffsets.append(offset);
|
|
|
|
|
} else {
|
|
|
|
|
nonAffectedMarkers.append(marker);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-10 18:01:33 +01:00
|
|
|
move.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
|
|
|
|
|
move.removeSelectedText();
|
2008-12-11 10:28:39 +01:00
|
|
|
|
2008-12-10 18:01:33 +01:00
|
|
|
if (up) {
|
|
|
|
|
move.movePosition(QTextCursor::PreviousBlock);
|
|
|
|
|
move.insertBlock();
|
|
|
|
|
move.movePosition(QTextCursor::Left);
|
|
|
|
|
} else {
|
|
|
|
|
move.movePosition(QTextCursor::EndOfBlock);
|
|
|
|
|
if (move.atBlockStart()) { // empty block
|
|
|
|
|
move.movePosition(QTextCursor::NextBlock);
|
|
|
|
|
move.insertBlock();
|
|
|
|
|
move.movePosition(QTextCursor::Left);
|
|
|
|
|
} else {
|
|
|
|
|
move.insertBlock();
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-12-11 10:28:39 +01:00
|
|
|
|
2008-12-10 18:01:33 +01:00
|
|
|
int start = move.position();
|
|
|
|
|
move.clearSelection();
|
|
|
|
|
move.insertText(text);
|
|
|
|
|
int end = move.position();
|
2008-12-11 13:20:59 +01:00
|
|
|
|
2008-12-10 18:01:33 +01:00
|
|
|
if (hasSelection) {
|
2013-01-09 21:41:24 +02:00
|
|
|
move.setPosition(end);
|
|
|
|
|
move.setPosition(start, QTextCursor::KeepAnchor);
|
|
|
|
|
} else {
|
2008-12-10 18:01:33 +01:00
|
|
|
move.setPosition(start);
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-07 14:34:40 +02:00
|
|
|
//update positions of affectedMarkers
|
|
|
|
|
for (int i=0;i < affectedMarkers.count(); i++) {
|
|
|
|
|
int newPosition = start + markerOffsets.at(i);
|
|
|
|
|
affectedMarkers[i].cursor.setPosition(newPosition);
|
|
|
|
|
}
|
2014-07-25 16:23:50 +02:00
|
|
|
m_refactorOverlay->setMarkers(nonAffectedMarkers + affectedMarkers);
|
2010-09-07 14:34:40 +02:00
|
|
|
|
2013-03-06 15:28:19 +01:00
|
|
|
bool shouldReindent = true;
|
2014-09-01 16:16:44 +02:00
|
|
|
if (m_commentDefinition.isValid()) {
|
2013-03-06 15:28:19 +01:00
|
|
|
QString trimmedText(text.trimmed());
|
|
|
|
|
|
2014-09-01 16:16:44 +02:00
|
|
|
if (m_commentDefinition.hasSingleLineStyle()) {
|
|
|
|
|
if (trimmedText.startsWith(m_commentDefinition.singleLine))
|
2013-03-06 15:28:19 +01:00
|
|
|
shouldReindent = false;
|
|
|
|
|
}
|
2014-09-01 16:16:44 +02:00
|
|
|
if (shouldReindent && m_commentDefinition.hasMultiLineStyle()) {
|
2013-03-06 15:28:19 +01:00
|
|
|
// Don't have any single line comments; try multi line.
|
2014-09-01 16:16:44 +02:00
|
|
|
if (trimmedText.startsWith(m_commentDefinition.multiLineStart)
|
|
|
|
|
&& trimmedText.endsWith(m_commentDefinition.multiLineEnd)) {
|
2013-03-06 15:28:19 +01:00
|
|
|
shouldReindent = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (shouldReindent) {
|
|
|
|
|
// The text was not commented at all; re-indent.
|
2014-07-25 16:23:50 +02:00
|
|
|
m_document->autoReindent(move);
|
2013-03-06 15:28:19 +01:00
|
|
|
}
|
2008-12-11 13:20:59 +01:00
|
|
|
move.endEditBlock();
|
|
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
q->setTextCursor(move);
|
|
|
|
|
m_moveLineUndoHack = true;
|
2008-12-10 18:01:33 +01:00
|
|
|
}
|
2008-12-04 18:37:02 +01:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::cleanWhitespace()
|
2008-12-09 17:43:31 +01:00
|
|
|
{
|
2009-09-09 16:37:09 +02:00
|
|
|
d->m_document->cleanWhitespace(textCursor());
|
2008-12-09 17:43:31 +01:00
|
|
|
}
|
2008-12-04 18:37:02 +01:00
|
|
|
|
2010-11-09 15:19:14 +01:00
|
|
|
|
|
|
|
|
// could go into QTextCursor...
|
|
|
|
|
static QTextLine currentTextLine(const QTextCursor &cursor)
|
|
|
|
|
{
|
|
|
|
|
const QTextBlock block = cursor.block();
|
|
|
|
|
if (!block.isValid())
|
|
|
|
|
return QTextLine();
|
|
|
|
|
|
|
|
|
|
const QTextLayout *layout = block.layout();
|
|
|
|
|
if (!layout)
|
|
|
|
|
return QTextLine();
|
|
|
|
|
|
|
|
|
|
const int relativePos = cursor.position() - block.position();
|
|
|
|
|
return layout->lineForTextPosition(relativePos);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidgetPrivate::camelCaseLeft(QTextCursor &cursor, QTextCursor::MoveMode mode)
|
2010-11-09 15:19:14 +01:00
|
|
|
{
|
|
|
|
|
int state = 0;
|
|
|
|
|
enum Input {
|
|
|
|
|
Input_U,
|
|
|
|
|
Input_l,
|
|
|
|
|
Input_underscore,
|
|
|
|
|
Input_space,
|
|
|
|
|
Input_other
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (!cursor.movePosition(QTextCursor::Left, mode))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
forever {
|
2014-07-25 16:23:50 +02:00
|
|
|
QChar c = q->document()->characterAt(cursor.position());
|
2010-11-09 15:19:14 +01:00
|
|
|
Input input = Input_other;
|
|
|
|
|
if (c.isUpper())
|
|
|
|
|
input = Input_U;
|
|
|
|
|
else if (c.isLower() || c.isDigit())
|
|
|
|
|
input = Input_l;
|
|
|
|
|
else if (c == QLatin1Char('_'))
|
|
|
|
|
input = Input_underscore;
|
|
|
|
|
else if (c.isSpace() && c != QChar::ParagraphSeparator)
|
|
|
|
|
input = Input_space;
|
|
|
|
|
else
|
|
|
|
|
input = Input_other;
|
|
|
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
|
case 0:
|
|
|
|
|
switch (input) {
|
|
|
|
|
case Input_U:
|
|
|
|
|
state = 1;
|
|
|
|
|
break;
|
|
|
|
|
case Input_l:
|
|
|
|
|
state = 2;
|
|
|
|
|
break;
|
|
|
|
|
case Input_underscore:
|
|
|
|
|
state = 3;
|
|
|
|
|
break;
|
|
|
|
|
case Input_space:
|
|
|
|
|
state = 4;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
cursor.movePosition(QTextCursor::Right, mode);
|
|
|
|
|
return cursor.movePosition(QTextCursor::WordLeft, mode);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
switch (input) {
|
|
|
|
|
case Input_U:
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2010-11-10 12:53:45 +01:00
|
|
|
cursor.movePosition(QTextCursor::Right, mode);
|
2010-11-09 15:19:14 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
|
switch (input) {
|
|
|
|
|
case Input_U:
|
|
|
|
|
return true;
|
|
|
|
|
case Input_l:
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
cursor.movePosition(QTextCursor::Right, mode);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 3:
|
|
|
|
|
switch (input) {
|
|
|
|
|
case Input_underscore:
|
|
|
|
|
break;
|
|
|
|
|
case Input_U:
|
|
|
|
|
state = 1;
|
|
|
|
|
break;
|
|
|
|
|
case Input_l:
|
|
|
|
|
state = 2;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
cursor.movePosition(QTextCursor::Right, mode);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 4:
|
|
|
|
|
switch (input) {
|
|
|
|
|
case Input_space:
|
|
|
|
|
break;
|
|
|
|
|
case Input_U:
|
|
|
|
|
state = 1;
|
|
|
|
|
break;
|
|
|
|
|
case Input_l:
|
|
|
|
|
state = 2;
|
|
|
|
|
break;
|
|
|
|
|
case Input_underscore:
|
|
|
|
|
state = 3;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
cursor.movePosition(QTextCursor::Right, mode);
|
|
|
|
|
if (cursor.positionInBlock() == 0)
|
|
|
|
|
return true;
|
|
|
|
|
return cursor.movePosition(QTextCursor::WordLeft, mode);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!cursor.movePosition(QTextCursor::Left, mode))
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidgetPrivate::camelCaseRight(QTextCursor &cursor, QTextCursor::MoveMode mode)
|
2010-11-09 15:19:14 +01:00
|
|
|
{
|
|
|
|
|
int state = 0;
|
|
|
|
|
enum Input {
|
|
|
|
|
Input_U,
|
|
|
|
|
Input_l,
|
|
|
|
|
Input_underscore,
|
|
|
|
|
Input_space,
|
|
|
|
|
Input_other
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
forever {
|
2014-07-25 16:23:50 +02:00
|
|
|
QChar c = q->document()->characterAt(cursor.position());
|
2010-11-09 15:19:14 +01:00
|
|
|
Input input = Input_other;
|
|
|
|
|
if (c.isUpper())
|
|
|
|
|
input = Input_U;
|
|
|
|
|
else if (c.isLower() || c.isDigit())
|
|
|
|
|
input = Input_l;
|
|
|
|
|
else if (c == QLatin1Char('_'))
|
|
|
|
|
input = Input_underscore;
|
|
|
|
|
else if (c.isSpace() && c != QChar::ParagraphSeparator)
|
|
|
|
|
input = Input_space;
|
|
|
|
|
else
|
|
|
|
|
input = Input_other;
|
|
|
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
|
case 0:
|
|
|
|
|
switch (input) {
|
|
|
|
|
case Input_U:
|
|
|
|
|
state = 4;
|
|
|
|
|
break;
|
|
|
|
|
case Input_l:
|
|
|
|
|
state = 1;
|
|
|
|
|
break;
|
|
|
|
|
case Input_underscore:
|
|
|
|
|
state = 6;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return cursor.movePosition(QTextCursor::WordRight, mode);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
switch (input) {
|
|
|
|
|
case Input_U:
|
|
|
|
|
return true;
|
|
|
|
|
case Input_l:
|
|
|
|
|
break;
|
|
|
|
|
case Input_underscore:
|
|
|
|
|
state = 6;
|
|
|
|
|
break;
|
|
|
|
|
case Input_space:
|
|
|
|
|
state = 7;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 2:
|
|
|
|
|
switch (input) {
|
|
|
|
|
case Input_U:
|
|
|
|
|
break;
|
|
|
|
|
case Input_l:
|
|
|
|
|
cursor.movePosition(QTextCursor::Left, mode);
|
|
|
|
|
return true;
|
2010-11-10 12:53:45 +01:00
|
|
|
case Input_underscore:
|
|
|
|
|
state = 6;
|
|
|
|
|
break;
|
2010-11-09 15:19:14 +01:00
|
|
|
case Input_space:
|
|
|
|
|
state = 7;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2010-11-10 12:26:18 +01:00
|
|
|
return true;
|
2010-11-09 15:19:14 +01:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 4:
|
|
|
|
|
switch (input) {
|
|
|
|
|
case Input_U:
|
|
|
|
|
state = 2;
|
|
|
|
|
break;
|
|
|
|
|
case Input_l:
|
|
|
|
|
state = 1;
|
|
|
|
|
break;
|
|
|
|
|
case Input_underscore:
|
|
|
|
|
state = 6;
|
|
|
|
|
break;
|
|
|
|
|
case Input_space:
|
|
|
|
|
state = 7;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2010-11-10 12:26:18 +01:00
|
|
|
return true;
|
2010-11-09 15:19:14 +01:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 6:
|
|
|
|
|
switch (input) {
|
|
|
|
|
case Input_underscore:
|
|
|
|
|
break;
|
|
|
|
|
case Input_space:
|
|
|
|
|
state = 7;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
2010-11-10 12:26:18 +01:00
|
|
|
return true;
|
2010-11-09 15:19:14 +01:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 7:
|
|
|
|
|
switch (input) {
|
|
|
|
|
case Input_space:
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
cursor.movePosition(QTextCursor::Right, mode);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidgetPrivate::cursorMoveKeyEvent(QKeyEvent *e)
|
2010-11-09 15:19:14 +01:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
QTextCursor cursor = q->textCursor();
|
2010-11-09 15:19:14 +01:00
|
|
|
|
|
|
|
|
QTextCursor::MoveMode mode = QTextCursor::MoveAnchor;
|
|
|
|
|
QTextCursor::MoveOperation op = QTextCursor::NoMove;
|
|
|
|
|
|
2013-07-17 00:01:45 +03:00
|
|
|
if (e == QKeySequence::MoveToNextChar) {
|
2010-11-09 15:19:14 +01:00
|
|
|
op = QTextCursor::Right;
|
2013-07-17 00:01:45 +03:00
|
|
|
} else if (e == QKeySequence::MoveToPreviousChar) {
|
2010-11-09 15:19:14 +01:00
|
|
|
op = QTextCursor::Left;
|
2013-07-17 00:01:45 +03:00
|
|
|
} else if (e == QKeySequence::SelectNextChar) {
|
2010-11-09 15:19:14 +01:00
|
|
|
op = QTextCursor::Right;
|
|
|
|
|
mode = QTextCursor::KeepAnchor;
|
2013-07-17 00:01:45 +03:00
|
|
|
} else if (e == QKeySequence::SelectPreviousChar) {
|
2010-11-09 15:19:14 +01:00
|
|
|
op = QTextCursor::Left;
|
|
|
|
|
mode = QTextCursor::KeepAnchor;
|
2013-07-17 00:01:45 +03:00
|
|
|
} else if (e == QKeySequence::SelectNextWord) {
|
2010-11-09 15:19:14 +01:00
|
|
|
op = QTextCursor::WordRight;
|
|
|
|
|
mode = QTextCursor::KeepAnchor;
|
2013-07-17 00:01:45 +03:00
|
|
|
} else if (e == QKeySequence::SelectPreviousWord) {
|
2010-11-09 15:19:14 +01:00
|
|
|
op = QTextCursor::WordLeft;
|
|
|
|
|
mode = QTextCursor::KeepAnchor;
|
2013-07-17 00:01:45 +03:00
|
|
|
} else if (e == QKeySequence::SelectStartOfLine) {
|
2010-11-09 15:19:14 +01:00
|
|
|
op = QTextCursor::StartOfLine;
|
|
|
|
|
mode = QTextCursor::KeepAnchor;
|
2013-07-17 00:01:45 +03:00
|
|
|
} else if (e == QKeySequence::SelectEndOfLine) {
|
2010-11-09 15:19:14 +01:00
|
|
|
op = QTextCursor::EndOfLine;
|
|
|
|
|
mode = QTextCursor::KeepAnchor;
|
2013-07-17 00:01:45 +03:00
|
|
|
} else if (e == QKeySequence::SelectStartOfBlock) {
|
2010-11-09 15:19:14 +01:00
|
|
|
op = QTextCursor::StartOfBlock;
|
|
|
|
|
mode = QTextCursor::KeepAnchor;
|
2013-07-17 00:01:45 +03:00
|
|
|
} else if (e == QKeySequence::SelectEndOfBlock) {
|
2010-11-09 15:19:14 +01:00
|
|
|
op = QTextCursor::EndOfBlock;
|
|
|
|
|
mode = QTextCursor::KeepAnchor;
|
2013-07-17 00:01:45 +03:00
|
|
|
} else if (e == QKeySequence::SelectStartOfDocument) {
|
2010-11-09 15:19:14 +01:00
|
|
|
op = QTextCursor::Start;
|
|
|
|
|
mode = QTextCursor::KeepAnchor;
|
2013-07-17 00:01:45 +03:00
|
|
|
} else if (e == QKeySequence::SelectEndOfDocument) {
|
2010-11-09 15:19:14 +01:00
|
|
|
op = QTextCursor::End;
|
|
|
|
|
mode = QTextCursor::KeepAnchor;
|
2013-07-17 00:01:45 +03:00
|
|
|
} else if (e == QKeySequence::SelectPreviousLine) {
|
2010-11-09 15:19:14 +01:00
|
|
|
op = QTextCursor::Up;
|
|
|
|
|
mode = QTextCursor::KeepAnchor;
|
2013-07-17 00:01:45 +03:00
|
|
|
} else if (e == QKeySequence::SelectNextLine) {
|
2010-11-09 15:19:14 +01:00
|
|
|
op = QTextCursor::Down;
|
|
|
|
|
mode = QTextCursor::KeepAnchor;
|
|
|
|
|
{
|
|
|
|
|
QTextBlock block = cursor.block();
|
|
|
|
|
QTextLine line = currentTextLine(cursor);
|
|
|
|
|
if (!block.next().isValid()
|
|
|
|
|
&& line.isValid()
|
|
|
|
|
&& line.lineNumber() == block.layout()->lineCount() - 1)
|
|
|
|
|
op = QTextCursor::End;
|
|
|
|
|
}
|
2013-07-17 00:01:45 +03:00
|
|
|
} else if (e == QKeySequence::MoveToNextWord) {
|
2010-11-09 15:19:14 +01:00
|
|
|
op = QTextCursor::WordRight;
|
2013-07-17 00:01:45 +03:00
|
|
|
} else if (e == QKeySequence::MoveToPreviousWord) {
|
2010-11-09 15:19:14 +01:00
|
|
|
op = QTextCursor::WordLeft;
|
2013-07-17 00:01:45 +03:00
|
|
|
} else if (e == QKeySequence::MoveToEndOfBlock) {
|
2010-11-09 15:19:14 +01:00
|
|
|
op = QTextCursor::EndOfBlock;
|
2013-07-17 00:01:45 +03:00
|
|
|
} else if (e == QKeySequence::MoveToStartOfBlock) {
|
2010-11-09 15:19:14 +01:00
|
|
|
op = QTextCursor::StartOfBlock;
|
2013-07-17 00:01:45 +03:00
|
|
|
} else if (e == QKeySequence::MoveToNextLine) {
|
2010-11-09 15:19:14 +01:00
|
|
|
op = QTextCursor::Down;
|
2013-07-17 00:01:45 +03:00
|
|
|
} else if (e == QKeySequence::MoveToPreviousLine) {
|
2010-11-09 15:19:14 +01:00
|
|
|
op = QTextCursor::Up;
|
2013-07-17 00:01:45 +03:00
|
|
|
} else if (e == QKeySequence::MoveToStartOfLine) {
|
2010-11-09 15:19:14 +01:00
|
|
|
op = QTextCursor::StartOfLine;
|
2013-07-17 00:01:45 +03:00
|
|
|
} else if (e == QKeySequence::MoveToEndOfLine) {
|
2010-11-09 15:19:14 +01:00
|
|
|
op = QTextCursor::EndOfLine;
|
2013-07-17 00:01:45 +03:00
|
|
|
} else if (e == QKeySequence::MoveToStartOfDocument) {
|
2010-11-09 15:19:14 +01:00
|
|
|
op = QTextCursor::Start;
|
2013-07-17 00:01:45 +03:00
|
|
|
} else if (e == QKeySequence::MoveToEndOfDocument) {
|
2010-11-09 15:19:14 +01:00
|
|
|
op = QTextCursor::End;
|
2013-07-17 00:01:45 +03:00
|
|
|
} else {
|
2010-11-09 15:19:14 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Except for pageup and pagedown, Mac OS X has very different behavior, we don't do it all, but
|
|
|
|
|
// here's the breakdown:
|
|
|
|
|
// Shift still works as an anchor, but only one of the other keys can be down Ctrl (Command),
|
|
|
|
|
// Alt (Option), or Meta (Control).
|
|
|
|
|
// Command/Control + Left/Right -- Move to left or right of the line
|
|
|
|
|
// + Up/Down -- Move to top bottom of the file. (Control doesn't move the cursor)
|
|
|
|
|
// Option + Left/Right -- Move one word Left/right.
|
|
|
|
|
// + Up/Down -- Begin/End of Paragraph.
|
|
|
|
|
// Home/End Top/Bottom of file. (usually don't move the cursor, but will select)
|
|
|
|
|
|
|
|
|
|
bool visualNavigation = cursor.visualNavigation();
|
|
|
|
|
cursor.setVisualNavigation(true);
|
|
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
if (q->camelCaseNavigationEnabled() && op == QTextCursor::WordRight)
|
2011-01-05 13:46:41 +01:00
|
|
|
camelCaseRight(cursor, mode);
|
2014-07-25 16:23:50 +02:00
|
|
|
else if (q->camelCaseNavigationEnabled() && op == QTextCursor::WordLeft)
|
2011-01-05 13:46:41 +01:00
|
|
|
camelCaseLeft(cursor, mode);
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
else if (!cursor.movePosition(op, mode) && mode == QTextCursor::MoveAnchor)
|
2012-01-23 14:18:08 +01:00
|
|
|
cursor.clearSelection();
|
2010-11-09 15:19:14 +01:00
|
|
|
cursor.setVisualNavigation(visualNavigation);
|
|
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
q->setTextCursor(cursor);
|
|
|
|
|
q->ensureCursorVisible();
|
2010-11-09 15:19:14 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::viewPageUp()
|
2014-06-02 14:21:46 +02:00
|
|
|
{
|
|
|
|
|
verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepSub);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::viewPageDown()
|
2014-06-02 14:21:46 +02:00
|
|
|
{
|
|
|
|
|
verticalScrollBar()->triggerAction(QAbstractSlider::SliderPageStepAdd);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::viewLineUp()
|
2014-06-02 14:21:46 +02:00
|
|
|
{
|
|
|
|
|
verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepSub);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::viewLineDown()
|
2014-06-02 14:21:46 +02:00
|
|
|
{
|
|
|
|
|
verticalScrollBar()->triggerAction(QAbstractSlider::SliderSingleStepAdd);
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-15 12:37:33 +01:00
|
|
|
static inline bool isModifier(QKeyEvent *e)
|
|
|
|
|
{
|
|
|
|
|
if (!e)
|
|
|
|
|
return false;
|
|
|
|
|
switch (e->key()) {
|
|
|
|
|
case Qt::Key_Shift:
|
|
|
|
|
case Qt::Key_Control:
|
|
|
|
|
case Qt::Key_Meta:
|
|
|
|
|
case Qt::Key_Alt:
|
|
|
|
|
return true;
|
|
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-11-09 15:19:14 +01:00
|
|
|
|
2014-05-26 10:02:07 +02:00
|
|
|
static inline bool isPrintableText(const QString &text)
|
|
|
|
|
{
|
|
|
|
|
return !text.isEmpty() && (text.at(0).isPrint() || text.at(0) == QLatin1Char('\t'));
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::keyPressEvent(QKeyEvent *e)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-02-10 13:23:59 +01:00
|
|
|
if (!isModifier(e) && mouseHidingEnabled())
|
2013-03-15 12:37:33 +01:00
|
|
|
viewport()->setCursor(Qt::BlankCursor);
|
2013-09-11 17:11:15 +02:00
|
|
|
ToolTip::hide();
|
2009-06-12 12:38:27 +02:00
|
|
|
|
2009-04-23 19:24:57 +02:00
|
|
|
d->m_moveLineUndoHack = false;
|
2010-05-20 15:10:26 +02:00
|
|
|
d->clearVisibleFoldedBlock();
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2011-12-08 13:23:41 +01:00
|
|
|
if (e->key() == Qt::Key_Alt
|
|
|
|
|
&& d->m_behaviorSettings.m_keyboardTooltips) {
|
|
|
|
|
d->m_maybeFakeTooltipEvent = true;
|
|
|
|
|
} else {
|
|
|
|
|
d->m_maybeFakeTooltipEvent = false;
|
|
|
|
|
if (e->key() == Qt::Key_Escape
|
|
|
|
|
&& d->m_snippetOverlay->isVisible()) {
|
2010-01-19 18:50:50 +01:00
|
|
|
e->accept();
|
|
|
|
|
d->m_snippetOverlay->hide();
|
2013-08-09 17:45:14 +02:00
|
|
|
d->m_snippetOverlay->mangle();
|
2010-01-19 18:50:50 +01:00
|
|
|
d->m_snippetOverlay->clear();
|
|
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
|
cursor.clearSelection();
|
|
|
|
|
setTextCursor(cursor);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool ro = isReadOnly();
|
2011-10-06 16:48:47 +02:00
|
|
|
const bool inOverwriteMode = overwriteMode();
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2014-06-16 14:20:36 +02:00
|
|
|
if (!ro && d->m_inBlockSelectionMode) {
|
2008-12-02 12:01:29 +01:00
|
|
|
if (e == QKeySequence::Cut) {
|
2014-06-16 14:20:36 +02:00
|
|
|
cut();
|
|
|
|
|
e->accept();
|
|
|
|
|
return;
|
2009-05-29 18:48:18 +02:00
|
|
|
} else if (e == QKeySequence::Delete || e->key() == Qt::Key_Backspace) {
|
2014-06-16 14:20:36 +02:00
|
|
|
if (d->m_blockSelection.positionColumn == d->m_blockSelection.anchorColumn) {
|
|
|
|
|
if (e == QKeySequence::Delete)
|
|
|
|
|
++d->m_blockSelection.positionColumn;
|
|
|
|
|
else if (d->m_blockSelection.positionColumn > 0)
|
|
|
|
|
--d->m_blockSelection.positionColumn;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2014-06-16 14:20:36 +02:00
|
|
|
d->removeBlockSelection();
|
|
|
|
|
e->accept();
|
|
|
|
|
return;
|
2008-12-04 18:37:02 +01:00
|
|
|
} else if (e == QKeySequence::Paste) {
|
2014-06-16 14:20:36 +02:00
|
|
|
d->removeBlockSelection();
|
|
|
|
|
// continue
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!ro
|
|
|
|
|
&& (e == QKeySequence::InsertParagraphSeparator
|
2011-04-15 16:19:23 +02:00
|
|
|
|| (!d->m_lineSeparatorsAllowed && e == QKeySequence::InsertLineSeparator))) {
|
2014-06-16 14:20:36 +02:00
|
|
|
if (d->m_inBlockSelectionMode) {
|
2016-12-21 08:44:10 +01:00
|
|
|
d->disableBlockSelection(TextEditorWidgetPrivate::CursorUpdateClearSelection);
|
2014-06-16 14:20:36 +02:00
|
|
|
e->accept();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-01-19 18:50:50 +01:00
|
|
|
if (d->m_snippetOverlay->isVisible()) {
|
|
|
|
|
e->accept();
|
|
|
|
|
d->m_snippetOverlay->hide();
|
2013-08-09 17:45:14 +02:00
|
|
|
d->m_snippetOverlay->mangle();
|
2010-01-19 18:50:50 +01:00
|
|
|
d->m_snippetOverlay->clear();
|
|
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
|
cursor.movePosition(QTextCursor::EndOfBlock);
|
|
|
|
|
setTextCursor(cursor);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
QTextCursor cursor = textCursor();
|
2009-09-09 13:55:11 +02:00
|
|
|
const TabSettings &ts = d->m_document->tabSettings();
|
2011-08-16 10:45:23 +02:00
|
|
|
const TypingSettings &tps = d->m_document->typingSettings();
|
2009-09-15 17:04:17 +02:00
|
|
|
cursor.beginEditBlock();
|
|
|
|
|
|
2017-07-17 12:41:37 +02:00
|
|
|
int extraBlocks = d->m_autoCompleter->paragraphSeparatorAboutToBeInserted(cursor);
|
2009-09-15 17:04:17 +02:00
|
|
|
|
2010-11-30 14:14:33 +01:00
|
|
|
QString previousIndentationString;
|
2011-08-16 10:45:23 +02:00
|
|
|
if (tps.m_autoIndent) {
|
2008-12-10 18:01:33 +01:00
|
|
|
cursor.insertBlock();
|
2014-01-15 17:09:52 +01:00
|
|
|
d->m_document->autoIndent(cursor);
|
2008-12-10 18:01:33 +01:00
|
|
|
} else {
|
|
|
|
|
cursor.insertBlock();
|
2009-12-03 15:46:05 +01:00
|
|
|
|
|
|
|
|
// After inserting the block, to avoid duplicating whitespace on the same line
|
2010-11-30 14:14:33 +01:00
|
|
|
const QString &previousBlockText = cursor.block().previous().text();
|
|
|
|
|
previousIndentationString = ts.indentationString(previousBlockText);
|
|
|
|
|
if (!previousIndentationString.isEmpty())
|
|
|
|
|
cursor.insertText(previousIndentationString);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2009-09-15 17:04:17 +02:00
|
|
|
cursor.endEditBlock();
|
2008-12-02 12:01:29 +01:00
|
|
|
e->accept();
|
2009-09-17 12:47:16 +02:00
|
|
|
|
|
|
|
|
if (extraBlocks > 0) {
|
|
|
|
|
QTextCursor ensureVisible = cursor;
|
|
|
|
|
while (extraBlocks > 0) {
|
|
|
|
|
--extraBlocks;
|
|
|
|
|
ensureVisible.movePosition(QTextCursor::NextBlock);
|
2011-08-16 10:45:23 +02:00
|
|
|
if (tps.m_autoIndent)
|
2014-01-15 17:09:52 +01:00
|
|
|
d->m_document->autoIndent(ensureVisible);
|
2010-11-30 14:14:33 +01:00
|
|
|
else if (!previousIndentationString.isEmpty())
|
|
|
|
|
ensureVisible.insertText(previousIndentationString);
|
2016-06-01 10:19:59 +02:00
|
|
|
if (d->m_animateAutoComplete || d->m_highlightAutoComplete) {
|
2016-04-28 12:51:34 +02:00
|
|
|
QTextCursor tc = ensureVisible;
|
|
|
|
|
tc.movePosition(QTextCursor::EndOfBlock);
|
|
|
|
|
tc.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
|
|
|
|
|
tc.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor);
|
2016-06-01 10:19:59 +02:00
|
|
|
d->autocompleterHighlight(tc);
|
2016-04-28 12:51:34 +02:00
|
|
|
}
|
2009-09-17 12:47:16 +02:00
|
|
|
}
|
|
|
|
|
setTextCursor(ensureVisible);
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
setTextCursor(cursor);
|
|
|
|
|
return;
|
2009-08-13 17:34:29 +02:00
|
|
|
} else if (!ro
|
|
|
|
|
&& (e == QKeySequence::MoveToStartOfBlock
|
|
|
|
|
|| e == QKeySequence::SelectStartOfBlock)){
|
2010-08-05 15:01:20 +02:00
|
|
|
if ((e->modifiers() & (Qt::AltModifier | Qt::ShiftModifier)) == (Qt::AltModifier | Qt::ShiftModifier)) {
|
|
|
|
|
e->accept();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2014-07-25 16:23:50 +02:00
|
|
|
d->handleHomeKey(e == QKeySequence::SelectStartOfBlock);
|
2009-08-13 17:34:29 +02:00
|
|
|
e->accept();
|
|
|
|
|
return;
|
|
|
|
|
} else if (!ro
|
|
|
|
|
&& (e == QKeySequence::MoveToStartOfLine
|
|
|
|
|
|| e == QKeySequence::SelectStartOfLine)){
|
2010-08-05 15:01:20 +02:00
|
|
|
if ((e->modifiers() & (Qt::AltModifier | Qt::ShiftModifier)) == (Qt::AltModifier | Qt::ShiftModifier)) {
|
|
|
|
|
e->accept();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2009-08-13 17:34:29 +02:00
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
|
if (QTextLayout *layout = cursor.block().layout()) {
|
|
|
|
|
if (layout->lineForTextPosition(cursor.position() - cursor.block().position()).lineNumber() == 0) {
|
2014-07-25 16:23:50 +02:00
|
|
|
d->handleHomeKey(e == QKeySequence::SelectStartOfLine);
|
2009-08-13 17:34:29 +02:00
|
|
|
e->accept();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-10-05 15:17:24 +02:00
|
|
|
} else if (!ro
|
|
|
|
|
&& e == QKeySequence::DeleteStartOfWord
|
2011-08-16 10:45:23 +02:00
|
|
|
&& d->m_document->typingSettings().m_autoIndent
|
2009-10-05 15:17:24 +02:00
|
|
|
&& !textCursor().hasSelection()){
|
|
|
|
|
e->accept();
|
|
|
|
|
QTextCursor c = textCursor();
|
|
|
|
|
int pos = c.position();
|
2011-09-19 19:00:53 +02:00
|
|
|
if (camelCaseNavigationEnabled())
|
2014-07-25 16:23:50 +02:00
|
|
|
d->camelCaseLeft(c, QTextCursor::MoveAnchor);
|
2011-09-19 19:00:53 +02:00
|
|
|
else
|
|
|
|
|
c.movePosition(QTextCursor::StartOfWord, QTextCursor::MoveAnchor);
|
2009-10-05 15:17:24 +02:00
|
|
|
int targetpos = c.position();
|
|
|
|
|
forever {
|
2014-07-25 16:23:50 +02:00
|
|
|
d->handleBackspaceKey();
|
2009-10-05 15:17:24 +02:00
|
|
|
int cpos = textCursor().position();
|
|
|
|
|
if (cpos == pos || cpos <= targetpos)
|
|
|
|
|
break;
|
|
|
|
|
pos = cpos;
|
|
|
|
|
}
|
|
|
|
|
return;
|
2010-11-10 12:26:18 +01:00
|
|
|
} else if (!ro && e == QKeySequence::DeleteStartOfWord && !textCursor().hasSelection()) {
|
|
|
|
|
e->accept();
|
|
|
|
|
QTextCursor c = textCursor();
|
2011-09-19 19:00:53 +02:00
|
|
|
if (camelCaseNavigationEnabled())
|
2014-07-25 16:23:50 +02:00
|
|
|
d->camelCaseLeft(c, QTextCursor::KeepAnchor);
|
2011-09-19 19:00:53 +02:00
|
|
|
else
|
|
|
|
|
c.movePosition(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
|
2010-11-10 12:26:18 +01:00
|
|
|
c.removeSelectedText();
|
|
|
|
|
return;
|
|
|
|
|
} else if (!ro && e == QKeySequence::DeleteEndOfWord && !textCursor().hasSelection()) {
|
|
|
|
|
e->accept();
|
|
|
|
|
QTextCursor c = textCursor();
|
2011-09-19 19:00:53 +02:00
|
|
|
if (camelCaseNavigationEnabled())
|
2014-07-25 16:23:50 +02:00
|
|
|
d->camelCaseRight(c, QTextCursor::KeepAnchor);
|
2011-09-19 19:00:53 +02:00
|
|
|
else
|
|
|
|
|
c.movePosition(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
|
2010-11-10 12:26:18 +01:00
|
|
|
c.removeSelectedText();
|
|
|
|
|
return;
|
2014-06-16 14:20:36 +02:00
|
|
|
} else if (!ro && (e == QKeySequence::MoveToNextPage || e == QKeySequence::MoveToPreviousPage)
|
|
|
|
|
&& d->m_inBlockSelectionMode) {
|
2016-12-21 08:44:10 +01:00
|
|
|
d->disableBlockSelection(TextEditorWidgetPrivate::CursorUpdateClearSelection);
|
2014-06-16 14:20:36 +02:00
|
|
|
QPlainTextEdit::keyPressEvent(e);
|
|
|
|
|
return;
|
|
|
|
|
} else if (!ro && (e == QKeySequence::SelectNextPage || e == QKeySequence::SelectPreviousPage)
|
|
|
|
|
&& d->m_inBlockSelectionMode) {
|
|
|
|
|
QPlainTextEdit::keyPressEvent(e);
|
|
|
|
|
d->m_blockSelection.positionBlock = QPlainTextEdit::textCursor().blockNumber();
|
2015-04-21 12:36:11 +02:00
|
|
|
doSetTextCursor(d->m_blockSelection.selection(d->m_document.data()), true);
|
2014-06-16 14:20:36 +02:00
|
|
|
viewport()->update();
|
|
|
|
|
e->accept();
|
|
|
|
|
return;
|
2008-12-02 12:01:29 +01:00
|
|
|
} else switch (e->key()) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#if 0
|
2009-11-25 15:55:45 +01:00
|
|
|
case Qt::Key_Dollar: {
|
|
|
|
|
d->m_overlay->setVisible(!d->m_overlay->isVisible());
|
|
|
|
|
d->m_overlay->setCursor(textCursor());
|
|
|
|
|
e->accept();
|
2008-12-02 12:01:29 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
} break;
|
|
|
|
|
#endif
|
|
|
|
|
case Qt::Key_Tab:
|
2009-05-18 12:21:30 -05:00
|
|
|
case Qt::Key_Backtab: {
|
2008-12-02 12:01:29 +01:00
|
|
|
if (ro) break;
|
2010-01-19 12:28:50 +01:00
|
|
|
if (d->m_snippetOverlay->isVisible() && !d->m_snippetOverlay->isEmpty()) {
|
|
|
|
|
d->snippetTabOrBacktab(e->key() == Qt::Key_Tab);
|
|
|
|
|
e->accept();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2009-05-18 12:21:30 -05:00
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
|
int newPosition;
|
2011-08-16 10:45:23 +02:00
|
|
|
if (d->m_document->typingSettings().tabShouldIndent(document(), cursor, &newPosition)) {
|
2009-05-18 12:21:30 -05:00
|
|
|
if (newPosition != cursor.position() && !cursor.hasSelection()) {
|
|
|
|
|
cursor.setPosition(newPosition);
|
|
|
|
|
setTextCursor(cursor);
|
|
|
|
|
}
|
2014-01-15 17:09:52 +01:00
|
|
|
d->m_document->autoIndent(cursor);
|
2009-05-18 12:21:30 -05:00
|
|
|
} else {
|
2015-07-16 15:56:25 +02:00
|
|
|
if (d->m_inBlockSelectionMode
|
|
|
|
|
&& d->m_blockSelection.firstVisualColumn() != d->m_blockSelection.lastVisualColumn()) {
|
|
|
|
|
d->removeBlockSelection();
|
|
|
|
|
} else {
|
|
|
|
|
if (e->key() == Qt::Key_Tab)
|
|
|
|
|
indent();
|
|
|
|
|
else
|
|
|
|
|
unindent();
|
|
|
|
|
}
|
2009-05-18 12:21:30 -05:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
e->accept();
|
|
|
|
|
return;
|
2009-05-18 12:21:30 -05:00
|
|
|
} break;
|
2008-12-02 12:01:29 +01:00
|
|
|
case Qt::Key_Backspace:
|
|
|
|
|
if (ro) break;
|
2009-09-16 15:39:51 +02:00
|
|
|
if ((e->modifiers() & (Qt::ControlModifier
|
2008-12-02 12:01:29 +01:00
|
|
|
| Qt::ShiftModifier
|
|
|
|
|
| Qt::AltModifier
|
|
|
|
|
| Qt::MetaModifier)) == Qt::NoModifier
|
|
|
|
|
&& !textCursor().hasSelection()) {
|
2014-07-25 16:23:50 +02:00
|
|
|
d->handleBackspaceKey();
|
2008-12-02 12:01:29 +01:00
|
|
|
e->accept();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case Qt::Key_Up:
|
|
|
|
|
case Qt::Key_Down:
|
|
|
|
|
case Qt::Key_Right:
|
|
|
|
|
case Qt::Key_Left:
|
2012-08-23 15:53:58 +02:00
|
|
|
if (HostOsInfo::isMacHost())
|
|
|
|
|
break;
|
2011-04-15 16:19:23 +02:00
|
|
|
if ((e->modifiers()
|
|
|
|
|
& (Qt::AltModifier | Qt::ShiftModifier)) == (Qt::AltModifier | Qt::ShiftModifier)) {
|
2014-06-16 14:20:36 +02:00
|
|
|
if (!d->m_inBlockSelectionMode)
|
|
|
|
|
d->enableBlockSelection(textCursor());
|
|
|
|
|
switch (e->key()) {
|
|
|
|
|
case Qt::Key_Up:
|
|
|
|
|
if (d->m_blockSelection.positionBlock > 0)
|
|
|
|
|
--d->m_blockSelection.positionBlock;
|
|
|
|
|
break;
|
|
|
|
|
case Qt::Key_Down:
|
|
|
|
|
if (d->m_blockSelection.positionBlock < document()->blockCount() - 1)
|
|
|
|
|
++d->m_blockSelection.positionBlock;
|
|
|
|
|
break;
|
|
|
|
|
case Qt::Key_Left:
|
|
|
|
|
if (d->m_blockSelection.positionColumn > 0)
|
|
|
|
|
--d->m_blockSelection.positionColumn;
|
|
|
|
|
break;
|
|
|
|
|
case Qt::Key_Right:
|
|
|
|
|
++d->m_blockSelection.positionColumn;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
d->resetCursorFlashTimer();
|
2015-04-21 12:36:11 +02:00
|
|
|
doSetTextCursor(d->m_blockSelection.selection(d->m_document.data()), true);
|
2014-06-16 14:20:36 +02:00
|
|
|
viewport()->update();
|
2010-08-05 15:01:20 +02:00
|
|
|
e->accept();
|
|
|
|
|
return;
|
2014-06-16 14:20:36 +02:00
|
|
|
} else if (d->m_inBlockSelectionMode) { // leave block selection mode
|
2016-12-21 08:44:10 +01:00
|
|
|
d->disableBlockSelection(TextEditorWidgetPrivate::NoCursorUpdate);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
break;
|
2011-10-06 16:48:47 +02:00
|
|
|
case Qt::Key_Insert:
|
|
|
|
|
if (ro) break;
|
2011-10-18 09:48:34 +02:00
|
|
|
if (e->modifiers() == Qt::NoModifier) {
|
2016-04-19 12:58:20 +02:00
|
|
|
setOverwriteMode(!inOverwriteMode);
|
2011-10-06 16:48:47 +02:00
|
|
|
e->accept();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-19 18:36:15 +03:00
|
|
|
const QString eventText = e->text();
|
2012-01-26 12:14:21 +01:00
|
|
|
if (!ro && d->m_inBlockSelectionMode) {
|
2014-06-16 14:20:36 +02:00
|
|
|
if (isPrintableText(eventText)) {
|
|
|
|
|
d->insertIntoBlockSelection(eventText);
|
2008-12-02 12:01:29 +01:00
|
|
|
goto skip_event;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-07 13:31:29 +02:00
|
|
|
if (e->key() == Qt::Key_H
|
|
|
|
|
&& e->modifiers() == Qt::KeyboardModifiers(HostOsInfo::controlModifier())) {
|
2014-07-25 16:23:50 +02:00
|
|
|
d->universalHelper();
|
2010-01-19 12:28:50 +01:00
|
|
|
e->accept();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-26 10:02:07 +02:00
|
|
|
if (ro || !isPrintableText(eventText)) {
|
2014-07-25 16:23:50 +02:00
|
|
|
if (!d->cursorMoveKeyEvent(e)) {
|
2010-11-02 15:35:42 +01:00
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
|
bool cursorWithinSnippet = false;
|
|
|
|
|
if (d->m_snippetOverlay->isVisible()
|
|
|
|
|
&& (e->key() == Qt::Key_Delete || e->key() == Qt::Key_Backspace)) {
|
|
|
|
|
cursorWithinSnippet = d->snippetCheckCursor(cursor);
|
|
|
|
|
}
|
|
|
|
|
if (cursorWithinSnippet)
|
|
|
|
|
cursor.beginEditBlock();
|
|
|
|
|
|
2010-11-09 15:19:14 +01:00
|
|
|
QPlainTextEdit::keyPressEvent(e);
|
2010-11-02 15:35:42 +01:00
|
|
|
|
|
|
|
|
if (cursorWithinSnippet) {
|
|
|
|
|
cursor.endEditBlock();
|
|
|
|
|
d->m_snippetOverlay->updateEquivalentSelections(textCursor());
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-01-20 10:12:11 +01:00
|
|
|
} else if ((e->modifiers() & (Qt::ControlModifier|Qt::AltModifier)) != Qt::ControlModifier){
|
2014-05-26 10:02:07 +02:00
|
|
|
// only go here if control is not pressed, except if also alt is pressed
|
|
|
|
|
// because AltGr maps to Alt + Ctrl
|
2009-09-15 17:04:17 +02:00
|
|
|
QTextCursor cursor = textCursor();
|
2016-06-10 15:13:38 +02:00
|
|
|
QString autoText;
|
|
|
|
|
if (!inOverwriteMode) {
|
2017-06-26 08:17:29 +02:00
|
|
|
const bool skipChar = d->m_skipAutoCompletedText
|
|
|
|
|
&& !d->m_autoCompleteHighlightPos.isEmpty()
|
|
|
|
|
&& cursor == d->m_autoCompleteHighlightPos.last();
|
|
|
|
|
autoText = autoCompleter()->autoComplete(cursor, eventText, skipChar);
|
2016-06-10 15:13:38 +02:00
|
|
|
}
|
2016-04-05 14:33:25 +02:00
|
|
|
const bool cursorWithinSnippet = d->snippetCheckCursor(cursor);
|
2009-09-16 15:31:11 +02:00
|
|
|
|
2009-09-15 17:04:17 +02:00
|
|
|
QChar electricChar;
|
2011-08-16 10:45:23 +02:00
|
|
|
if (d->m_document->typingSettings().m_autoIndent) {
|
2014-05-19 18:36:15 +03:00
|
|
|
foreach (QChar c, eventText) {
|
2014-01-13 16:56:53 +01:00
|
|
|
if (d->m_document->indenter()->isElectricCharacter(c)) {
|
2009-09-15 17:04:17 +02:00
|
|
|
electricChar = c;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-09-17 13:33:42 +02:00
|
|
|
|
2010-11-02 15:35:42 +01:00
|
|
|
bool doEditBlock = !electricChar.isNull() || !autoText.isEmpty() || cursorWithinSnippet;
|
2009-09-17 13:33:42 +02:00
|
|
|
if (doEditBlock)
|
2009-09-15 17:04:17 +02:00
|
|
|
cursor.beginEditBlock();
|
2009-09-16 13:56:34 +02:00
|
|
|
|
2011-10-06 16:48:47 +02:00
|
|
|
if (inOverwriteMode) {
|
|
|
|
|
if (!doEditBlock)
|
|
|
|
|
cursor.beginEditBlock();
|
|
|
|
|
QTextBlock block = cursor.block();
|
|
|
|
|
int eolPos = block.position() + block.length() - 1;
|
2014-05-19 18:36:15 +03:00
|
|
|
int selEndPos = qMin(cursor.position() + eventText.length(), eolPos);
|
2011-10-06 16:48:47 +02:00
|
|
|
cursor.setPosition(selEndPos, QTextCursor::KeepAnchor);
|
2014-05-19 18:36:15 +03:00
|
|
|
cursor.insertText(eventText);
|
2011-10-06 16:48:47 +02:00
|
|
|
if (!doEditBlock)
|
|
|
|
|
cursor.endEditBlock();
|
|
|
|
|
} else {
|
2014-05-19 18:36:15 +03:00
|
|
|
cursor.insertText(eventText);
|
2011-10-06 16:48:47 +02:00
|
|
|
}
|
2009-09-16 13:56:34 +02:00
|
|
|
|
2009-09-17 12:36:40 +02:00
|
|
|
if (!autoText.isEmpty()) {
|
2009-09-15 17:04:17 +02:00
|
|
|
int pos = cursor.position();
|
|
|
|
|
cursor.insertText(autoText);
|
2016-06-01 10:19:59 +02:00
|
|
|
cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
|
|
|
|
|
d->autocompleterHighlight(cursor);
|
2010-11-24 19:44:54 +01:00
|
|
|
//Select the inserted text, to be able to re-indent the inserted text
|
|
|
|
|
cursor.setPosition(pos, QTextCursor::KeepAnchor);
|
2009-09-15 17:04:17 +02:00
|
|
|
}
|
2014-08-19 12:12:45 +02:00
|
|
|
if (!electricChar.isNull() && d->m_autoCompleter->contextAllowsElectricCharacters(cursor))
|
2014-01-15 17:09:52 +01:00
|
|
|
d->m_document->autoIndent(cursor, electricChar);
|
2013-11-11 22:20:47 +02:00
|
|
|
if (!autoText.isEmpty())
|
2010-11-24 19:44:54 +01:00
|
|
|
cursor.setPosition(autoText.length() == 1 ? cursor.position() : cursor.anchor());
|
2009-09-17 13:33:42 +02:00
|
|
|
|
2010-11-02 15:35:42 +01:00
|
|
|
if (doEditBlock) {
|
2009-09-15 17:04:17 +02:00
|
|
|
cursor.endEditBlock();
|
2010-11-02 15:35:42 +01:00
|
|
|
if (cursorWithinSnippet)
|
|
|
|
|
d->m_snippetOverlay->updateEquivalentSelections(textCursor());
|
|
|
|
|
}
|
2009-09-17 13:33:42 +02:00
|
|
|
|
2009-09-15 17:04:17 +02:00
|
|
|
setTextCursor(cursor);
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-07-19 14:06:00 +02:00
|
|
|
skip_event:
|
2008-12-02 12:01:29 +01:00
|
|
|
if (!ro && e->key() == Qt::Key_Delete && d->m_parenthesesMatchingEnabled)
|
2013-05-24 12:53:35 +02:00
|
|
|
d->m_parenthesesMatchingTimer.start(50);
|
2009-05-06 11:18:56 +02:00
|
|
|
|
2014-05-26 10:02:07 +02:00
|
|
|
if (!ro && d->m_contentsChanged && isPrintableText(eventText) && !inOverwriteMode)
|
2014-08-20 15:31:39 +02:00
|
|
|
d->m_codeAssistant.process();
|
2010-07-19 14:06:00 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::insertCodeSnippet(const QTextCursor &cursor_arg, const QString &snippet)
|
2010-01-19 12:28:50 +01:00
|
|
|
{
|
2013-08-20 13:58:16 +02:00
|
|
|
Snippet::ParsedSnippet data = Snippet::parse(snippet);
|
2010-03-29 16:44:27 +02:00
|
|
|
|
2015-09-25 10:31:28 +02:00
|
|
|
if (!data.success) {
|
|
|
|
|
QString message = QString::fromLatin1("Cannot parse snippet \"%1\".").arg(snippet);
|
|
|
|
|
if (!data.errorMessage.isEmpty())
|
|
|
|
|
message += QLatin1String("\nParse error: ") + data.errorMessage;
|
|
|
|
|
QMessageBox::warning(this, QLatin1String("Snippet Parse Error"), message);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2010-03-29 16:44:27 +02:00
|
|
|
QTextCursor cursor = cursor_arg;
|
|
|
|
|
cursor.beginEditBlock();
|
|
|
|
|
cursor.removeSelectedText();
|
|
|
|
|
const int startCursorPosition = cursor.position();
|
|
|
|
|
|
2013-08-20 13:58:16 +02:00
|
|
|
cursor.insertText(data.text);
|
|
|
|
|
QList<QTextEdit::ExtraSelection> selections;
|
2010-01-19 15:59:09 +01:00
|
|
|
|
2013-08-09 17:45:14 +02:00
|
|
|
QList<NameMangler *> manglers;
|
2013-08-20 13:58:16 +02:00
|
|
|
for (int i = 0; i < data.ranges.count(); ++i) {
|
|
|
|
|
int position = data.ranges.at(i).start + startCursorPosition;
|
|
|
|
|
int length = data.ranges.at(i).length;
|
2010-01-19 16:18:28 +01:00
|
|
|
|
|
|
|
|
QTextCursor tc(document());
|
|
|
|
|
tc.setPosition(position);
|
|
|
|
|
tc.setPosition(position + length, QTextCursor::KeepAnchor);
|
|
|
|
|
QTextEdit::ExtraSelection selection;
|
|
|
|
|
selection.cursor = tc;
|
2014-01-21 14:29:25 +01:00
|
|
|
selection.format = (length
|
2014-08-01 23:31:56 +02:00
|
|
|
? textDocument()->fontSettings().toTextCharFormat(C_OCCURRENCES)
|
|
|
|
|
: textDocument()->fontSettings().toTextCharFormat(C_OCCURRENCES_RENAME));
|
2010-01-19 16:18:28 +01:00
|
|
|
selections.append(selection);
|
2013-08-09 17:45:14 +02:00
|
|
|
manglers << data.ranges.at(i).mangler;
|
2010-01-19 16:18:28 +01:00
|
|
|
}
|
|
|
|
|
|
2010-01-19 15:59:09 +01:00
|
|
|
cursor.setPosition(startCursorPosition, QTextCursor::KeepAnchor);
|
2014-01-15 17:09:52 +01:00
|
|
|
d->m_document->autoIndent(cursor);
|
2010-01-19 16:18:28 +01:00
|
|
|
cursor.endEditBlock();
|
2010-01-19 15:59:09 +01:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
setExtraSelections(TextEditorWidget::SnippetPlaceholderSelection, selections);
|
2013-08-09 17:45:14 +02:00
|
|
|
d->m_snippetOverlay->setNameMangler(manglers);
|
2010-01-19 15:59:09 +01:00
|
|
|
|
2013-08-20 13:58:09 +02:00
|
|
|
if (!selections.isEmpty()) {
|
2010-01-19 15:59:09 +01:00
|
|
|
const QTextEdit::ExtraSelection &selection = selections.first();
|
|
|
|
|
|
|
|
|
|
cursor = textCursor();
|
2010-01-19 18:50:50 +01:00
|
|
|
if (selection.cursor.hasSelection()) {
|
2010-08-24 12:28:51 +02:00
|
|
|
cursor.setPosition(selection.cursor.selectionStart());
|
2010-01-19 18:50:50 +01:00
|
|
|
cursor.setPosition(selection.cursor.selectionEnd(), QTextCursor::KeepAnchor);
|
|
|
|
|
} else {
|
|
|
|
|
cursor.setPosition(selection.cursor.position());
|
|
|
|
|
}
|
2010-01-19 15:59:09 +01:00
|
|
|
setTextCursor(cursor);
|
|
|
|
|
}
|
2010-01-19 12:28:50 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::universalHelper()
|
2010-01-19 16:36:48 +01:00
|
|
|
{
|
2010-02-03 14:58:09 +01:00
|
|
|
// Test function for development. Place your new fangled experiment here to
|
|
|
|
|
// give it proper scrutiny before pushing it onto others.
|
2010-01-19 16:36:48 +01:00
|
|
|
}
|
|
|
|
|
|
2015-04-21 12:36:11 +02:00
|
|
|
void TextEditorWidget::doSetTextCursor(const QTextCursor &cursor, bool keepBlockSelection)
|
2008-12-04 18:37:02 +01:00
|
|
|
{
|
|
|
|
|
// workaround for QTextControl bug
|
|
|
|
|
bool selectionChange = cursor.hasSelection() || textCursor().hasSelection();
|
2014-06-16 14:20:36 +02:00
|
|
|
if (!keepBlockSelection && d->m_inBlockSelectionMode)
|
2016-12-21 08:44:10 +01:00
|
|
|
d->disableBlockSelection(TextEditorWidgetPrivate::NoCursorUpdate);
|
2009-12-03 19:58:44 +01:00
|
|
|
QTextCursor c = cursor;
|
|
|
|
|
c.setVisualNavigation(true);
|
2015-04-21 12:36:11 +02:00
|
|
|
QPlainTextEdit::doSetTextCursor(c);
|
2008-12-04 18:37:02 +01:00
|
|
|
if (selectionChange)
|
2014-08-01 15:06:28 +02:00
|
|
|
d->slotSelectionChanged();
|
2008-12-04 18:37:02 +01:00
|
|
|
}
|
|
|
|
|
|
2015-04-21 12:36:11 +02:00
|
|
|
void TextEditorWidget::doSetTextCursor(const QTextCursor &cursor)
|
2014-06-16 14:20:36 +02:00
|
|
|
{
|
2015-04-21 12:36:11 +02:00
|
|
|
doSetTextCursor(cursor, false);
|
2014-06-16 14:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
2017-05-23 14:34:22 +02:00
|
|
|
void TextEditorWidget::gotoLine(int line, int column, bool centerLine, bool animate)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-03-19 15:17:05 +01:00
|
|
|
d->m_lastCursorChangeWasInteresting = false; // avoid adding the previous position to history
|
2015-08-04 21:24:31 +03:00
|
|
|
const int blockNumber = qMin(line, document()->blockCount()) - 1;
|
2008-12-02 12:01:29 +01:00
|
|
|
const QTextBlock &block = document()->findBlockByNumber(blockNumber);
|
|
|
|
|
if (block.isValid()) {
|
|
|
|
|
QTextCursor cursor(block);
|
|
|
|
|
if (column > 0) {
|
|
|
|
|
cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, column);
|
|
|
|
|
} else {
|
|
|
|
|
int pos = cursor.position();
|
2013-04-18 18:21:17 +02:00
|
|
|
while (document()->characterAt(pos).category() == QChar::Separator_Space) {
|
2008-12-02 12:01:29 +01:00
|
|
|
++pos;
|
|
|
|
|
}
|
|
|
|
|
cursor.setPosition(pos);
|
|
|
|
|
}
|
2014-05-02 12:08:44 +02:00
|
|
|
|
2017-05-23 14:34:22 +02:00
|
|
|
const DisplaySettings &ds = d->m_displaySettings;
|
|
|
|
|
if (animate && ds.m_animateNavigationWithinFile) {
|
|
|
|
|
const QScrollBar *scrollBar = verticalScrollBar();
|
|
|
|
|
const int start = scrollBar->value();
|
|
|
|
|
|
|
|
|
|
setTextCursor(cursor);
|
|
|
|
|
ensureBlockIsUnfolded(block);
|
|
|
|
|
|
|
|
|
|
const int visibleLines = lastVisibleLine() - firstVisibleLine();
|
|
|
|
|
|
|
|
|
|
int end = 0;
|
|
|
|
|
auto it = document()->firstBlock();
|
|
|
|
|
while (it.isValid() && it != block) {
|
|
|
|
|
if (it.isVisible())
|
|
|
|
|
++end;
|
|
|
|
|
it = it.next();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (centerLine)
|
|
|
|
|
end = qMin(scrollBar->maximum(), qMax(scrollBar->minimum(), end - visibleLines / 2));
|
|
|
|
|
|
|
|
|
|
const int delta = end - start;
|
|
|
|
|
// limit the number of steps for the animation otherwise you wont be able to tell
|
|
|
|
|
// the direction of the animantion for large delta values
|
|
|
|
|
const int steps = qMax(-ds.m_animateWithinFileTimeMax,
|
|
|
|
|
qMin(ds.m_animateWithinFileTimeMax, delta));
|
|
|
|
|
// limit the duration of the animation to at least 4 pictures on a 60Hz Monitor and
|
|
|
|
|
// at most to the number of absolute steps
|
|
|
|
|
const int durationMinimum = int (4 // number of pictures
|
|
|
|
|
* float(1) / 60 // on a 60 Hz Monitor
|
|
|
|
|
* 1000); // milliseconds
|
|
|
|
|
const int duration = qMax(durationMinimum, qAbs(steps));
|
|
|
|
|
|
|
|
|
|
d->m_navigationAnimation = new QSequentialAnimationGroup(this);
|
|
|
|
|
auto startAnimation = new QPropertyAnimation(verticalScrollBar(), "value");
|
|
|
|
|
startAnimation->setEasingCurve(QEasingCurve::InExpo);
|
|
|
|
|
startAnimation->setStartValue(start);
|
|
|
|
|
startAnimation->setEndValue(start + steps / 2);
|
|
|
|
|
startAnimation->setDuration(duration / 2);
|
|
|
|
|
d->m_navigationAnimation->addAnimation(startAnimation);
|
|
|
|
|
auto endAnimation = new QPropertyAnimation(verticalScrollBar(), "value");
|
|
|
|
|
endAnimation->setEasingCurve(QEasingCurve::OutExpo);
|
|
|
|
|
endAnimation->setStartValue(end - steps / 2);
|
|
|
|
|
endAnimation->setEndValue(end);
|
|
|
|
|
endAnimation->setDuration(duration / 2);
|
|
|
|
|
d->m_navigationAnimation->addAnimation(endAnimation);
|
|
|
|
|
d->m_navigationAnimation->start(QAbstractAnimation::DeleteWhenStopped);
|
|
|
|
|
} else {
|
|
|
|
|
setTextCursor(cursor);
|
|
|
|
|
if (centerLine)
|
|
|
|
|
centerCursor();
|
|
|
|
|
else
|
|
|
|
|
ensureCursorVisible();
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2014-07-25 16:23:50 +02:00
|
|
|
d->saveCurrentCursorPositionForNavigation();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-30 17:45:48 +02:00
|
|
|
int TextEditorWidget::position(TextPositionOperation posOp, int at) const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
QTextCursor tc = textCursor();
|
|
|
|
|
|
|
|
|
|
if (at != -1)
|
|
|
|
|
tc.setPosition(at);
|
|
|
|
|
|
2014-09-30 17:45:48 +02:00
|
|
|
if (posOp == CurrentPosition)
|
2008-12-02 12:01:29 +01:00
|
|
|
return tc.position();
|
|
|
|
|
|
|
|
|
|
switch (posOp) {
|
2014-09-30 17:45:48 +02:00
|
|
|
case EndOfLinePosition:
|
2008-12-02 12:01:29 +01:00
|
|
|
tc.movePosition(QTextCursor::EndOfLine);
|
|
|
|
|
return tc.position();
|
2014-09-30 17:45:48 +02:00
|
|
|
case StartOfLinePosition:
|
2008-12-02 12:01:29 +01:00
|
|
|
tc.movePosition(QTextCursor::StartOfLine);
|
|
|
|
|
return tc.position();
|
2014-09-30 17:45:48 +02:00
|
|
|
case AnchorPosition:
|
2008-12-02 12:01:29 +01:00
|
|
|
if (tc.hasSelection())
|
|
|
|
|
return tc.anchor();
|
|
|
|
|
break;
|
2014-09-30 17:45:48 +02:00
|
|
|
case EndOfDocPosition:
|
2008-12-02 12:01:29 +01:00
|
|
|
tc.movePosition(QTextCursor::End);
|
|
|
|
|
return tc.position();
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
QRect TextEditorWidget::cursorRect(int pos) const
|
2014-09-03 08:35:58 +02:00
|
|
|
{
|
|
|
|
|
QTextCursor tc = textCursor();
|
|
|
|
|
if (pos >= 0)
|
|
|
|
|
tc.setPosition(pos);
|
|
|
|
|
QRect result = cursorRect(tc);
|
|
|
|
|
result.moveTo(viewport()->mapToGlobal(result.topLeft()));
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::convertPosition(int pos, int *line, int *column) const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2017-09-21 12:35:24 +02:00
|
|
|
Text::convertPosition(document(), pos, line, column);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::event(QEvent *e)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2016-12-05 15:13:35 +01:00
|
|
|
if (!d)
|
|
|
|
|
return QPlainTextEdit::event(e);
|
|
|
|
|
|
2014-08-20 23:04:08 +02:00
|
|
|
// FIXME: That's far too heavy, and triggers e.g for ChildEvent
|
2017-01-05 10:25:33 +01:00
|
|
|
if (e->type() != QEvent::InputMethodQuery)
|
2016-12-05 15:13:35 +01:00
|
|
|
d->m_contentsChanged = false;
|
2017-01-05 10:25:33 +01:00
|
|
|
switch (e->type()) {
|
2013-10-09 17:09:49 +02:00
|
|
|
case QEvent::ShortcutOverride:
|
|
|
|
|
if (static_cast<QKeyEvent*>(e)->key() == Qt::Key_Escape && d->m_snippetOverlay->isVisible()) {
|
|
|
|
|
e->accept();
|
2016-12-05 15:13:35 +01:00
|
|
|
} else {
|
|
|
|
|
e->ignore(); // we are a really nice citizen
|
|
|
|
|
d->m_maybeFakeTooltipEvent = false;
|
2013-10-09 17:09:49 +02:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
return true;
|
Implement theming for QtCreator
Adds a 'Theme' tab to the environment settings and a '-theme' command
line option.
A theme is a combination of colors, gradients, flags and style
information.
There are two themes:
- 'default': preserves the current default look
- 'dark': uses a more flat for many widgets, dark color theme
for everything
This does not use a stylesheet (too limited), but rather sets
the palette via C++ and modifies drawing behavior.
Overall, the look is more flat (removed some gradients and bevels).
Tested on Ubuntu 14.04 using Qt 5.4 and running on a KDE
Desktop (Oxygen base style).
For a screenshot, see
https://gist.github.com/thorbenk/5ab06bea726de0aa7473
Changes:
- Introduce class Theme, defining the interface how to access theme
specific settings. The class reads a .creatortheme file (INI file, via
QSettings)
- Define named colors in the [Palette] section
(see dark.creatortheme for example usage)
- Use either named colors of AARRGGBB (hex) in the [Colors]
section
- A file ending with .creatortheme may be supplied
to the '-theme' command line option
- A global Theme instance can be accessed via creatorTheme()
- Query colors, gradients, icons and flags from the theme
were possible (TODO: use this in more places...)
- There are very many color roles. It seems better to me
to describe the role clearly, and then to consolidate later
in the actual theme by assigning the same color.
For example, one can set the text color of the output pane button
individualy.
- Many elements are also drawn differently.
For the dark theme, I wanted to have a flatter look.
- Introduce Theme::WidgetStyle enum, for now {Original, Flat}.
- The theme specifies which kind of widget style it wants.
- The drawing code queries the theme's style flag and
switches between the original, gradient based look and
the new, flat look.
- Create some custom icons which look better on dark background
(wip, currently folder/file icons)
- Let ManhattanStyle draw some elements for non-panelwidgets, too
(open/close arrows in QTreeView, custom folder/file icons)
- For the welcomescreen, pass the WelcomeTheme class.
WelcomeTheme exposes theme colors as Q_PROPERTY accessible from
.qml
- Themes can be modified via the 'Themes' tab in the environment
settings.
TODO:
* Unify image handling
* Avoid style name references
* Fix gradients
Change-Id: I92c2050ab0fb327649ea1eff4adec973d2073944
Reviewed-by: Thomas Hartmann <Thomas.Hartmann@digia.com>
Reviewed-by: hjk <hjk121@nokiamail.com>
2014-10-14 19:09:48 +02:00
|
|
|
case QEvent::ApplicationPaletteChange: {
|
|
|
|
|
// slight hack: ignore palette changes
|
|
|
|
|
// at this point the palette has changed already,
|
|
|
|
|
// so undo it by re-setting the palette:
|
|
|
|
|
applyFontSettings();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2013-10-09 17:09:49 +02:00
|
|
|
default:
|
|
|
|
|
break;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2013-10-09 17:09:49 +02:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
return QPlainTextEdit::event(e);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::inputMethodEvent(QInputMethodEvent *e)
|
2014-06-16 14:20:36 +02:00
|
|
|
{
|
|
|
|
|
if (d->m_inBlockSelectionMode) {
|
|
|
|
|
if (!e->commitString().isEmpty())
|
|
|
|
|
d->insertIntoBlockSelection(e->commitString());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
QPlainTextEdit::inputMethodEvent(e);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::documentAboutToBeReloaded()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-06-24 20:13:13 +02:00
|
|
|
//memorize cursor position
|
2014-08-01 15:06:28 +02:00
|
|
|
m_tempState = q->saveState();
|
2010-06-24 20:13:13 +02:00
|
|
|
|
|
|
|
|
// remove extra selections (loads of QTextCursor objects)
|
|
|
|
|
|
2014-10-21 17:50:10 +02:00
|
|
|
m_extraSelections.clear();
|
2015-06-23 15:56:45 +02:00
|
|
|
m_extraSelections.reserve(NExtraSelectionKinds);
|
2014-08-01 15:06:28 +02:00
|
|
|
q->QPlainTextEdit::setExtraSelections(QList<QTextEdit::ExtraSelection>());
|
2010-06-24 20:13:13 +02:00
|
|
|
|
|
|
|
|
// clear all overlays
|
2014-08-01 15:06:28 +02:00
|
|
|
m_overlay->clear();
|
|
|
|
|
m_snippetOverlay->clear();
|
|
|
|
|
m_searchResultOverlay->clear();
|
|
|
|
|
m_refactorOverlay->clear();
|
2015-06-04 10:43:34 +02:00
|
|
|
|
|
|
|
|
// clear search results
|
|
|
|
|
m_searchResults.clear();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::documentReloadFinished(bool success)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2013-06-20 12:49:19 +02:00
|
|
|
if (!success)
|
|
|
|
|
return;
|
|
|
|
|
|
2010-06-24 20:13:13 +02:00
|
|
|
// restore cursor position
|
2014-08-01 15:06:28 +02:00
|
|
|
q->restoreState(m_tempState);
|
|
|
|
|
updateCannotDecodeInfo();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
QByteArray TextEditorWidget::saveState() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
QByteArray state;
|
|
|
|
|
QDataStream stream(&state, QIODevice::WriteOnly);
|
2010-03-15 12:26:16 +01:00
|
|
|
stream << 1; // version number
|
2008-12-02 12:01:29 +01:00
|
|
|
stream << verticalScrollBar()->value();
|
|
|
|
|
stream << horizontalScrollBar()->value();
|
|
|
|
|
int line, column;
|
|
|
|
|
convertPosition(textCursor().position(), &line, &column);
|
|
|
|
|
stream << line;
|
|
|
|
|
stream << column;
|
2010-03-15 12:26:16 +01:00
|
|
|
|
|
|
|
|
// store code folding state
|
2010-05-20 15:10:26 +02:00
|
|
|
QList<int> foldedBlocks;
|
2010-03-15 12:26:16 +01:00
|
|
|
QTextBlock block = document()->firstBlock();
|
|
|
|
|
while (block.isValid()) {
|
2010-05-20 15:10:26 +02:00
|
|
|
if (block.userData() && static_cast<TextBlockUserData*>(block.userData())->folded()) {
|
2010-03-15 12:26:16 +01:00
|
|
|
int number = block.blockNumber();
|
2010-05-20 15:10:26 +02:00
|
|
|
foldedBlocks += number;
|
2010-03-15 12:26:16 +01:00
|
|
|
}
|
|
|
|
|
block = block.next();
|
|
|
|
|
}
|
2010-05-20 15:10:26 +02:00
|
|
|
stream << foldedBlocks;
|
2010-03-15 12:26:16 +01:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
return state;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::restoreState(const QByteArray &state)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-03-15 13:16:40 +01:00
|
|
|
if (state.isEmpty()) {
|
|
|
|
|
if (d->m_displaySettings.m_autoFoldFirstComment)
|
2010-05-20 15:10:26 +02:00
|
|
|
d->foldLicenseHeader();
|
2010-03-15 13:16:40 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
int version;
|
|
|
|
|
int vval;
|
|
|
|
|
int hval;
|
|
|
|
|
int lval;
|
|
|
|
|
int cval;
|
|
|
|
|
QDataStream stream(state);
|
|
|
|
|
stream >> version;
|
|
|
|
|
stream >> vval;
|
|
|
|
|
stream >> hval;
|
|
|
|
|
stream >> lval;
|
|
|
|
|
stream >> cval;
|
2010-03-15 12:26:16 +01:00
|
|
|
|
|
|
|
|
if (version >= 1) {
|
|
|
|
|
QList<int> collapsedBlocks;
|
|
|
|
|
stream >> collapsedBlocks;
|
|
|
|
|
QTextDocument *doc = document();
|
2011-12-09 17:36:42 +01:00
|
|
|
bool layoutChanged = false;
|
2012-11-28 20:44:03 +02:00
|
|
|
foreach (int blockNumber, collapsedBlocks) {
|
2010-03-15 12:26:16 +01:00
|
|
|
QTextBlock block = doc->findBlockByNumber(qMax(0, blockNumber));
|
2011-12-09 17:36:42 +01:00
|
|
|
if (block.isValid()) {
|
2014-09-26 09:14:03 +02:00
|
|
|
TextDocumentLayout::doFoldOrUnfold(block, false);
|
2011-12-09 17:36:42 +01:00
|
|
|
layoutChanged = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (layoutChanged) {
|
2014-09-26 09:14:03 +02:00
|
|
|
TextDocumentLayout *documentLayout =
|
|
|
|
|
qobject_cast<TextDocumentLayout*>(doc->documentLayout());
|
2011-12-09 17:36:42 +01:00
|
|
|
QTC_ASSERT(documentLayout, return false);
|
|
|
|
|
documentLayout->requestUpdate();
|
|
|
|
|
documentLayout->emitDocumentSizeChanged();
|
2010-03-15 12:26:16 +01:00
|
|
|
}
|
2010-03-15 13:16:40 +01:00
|
|
|
} else {
|
|
|
|
|
if (d->m_displaySettings.m_autoFoldFirstComment)
|
2010-05-20 15:10:26 +02:00
|
|
|
d->foldLicenseHeader();
|
2010-03-15 12:26:16 +01:00
|
|
|
}
|
|
|
|
|
|
2009-03-19 15:01:12 +01:00
|
|
|
d->m_lastCursorChangeWasInteresting = false; // avoid adding last position to history
|
|
|
|
|
gotoLine(lval, cval);
|
2008-12-02 12:01:29 +01:00
|
|
|
verticalScrollBar()->setValue(vval);
|
|
|
|
|
horizontalScrollBar()->setValue(hval);
|
2014-07-25 16:23:50 +02:00
|
|
|
d->saveCurrentCursorPositionForNavigation();
|
2008-12-02 12:01:29 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setParenthesesMatchingEnabled(bool b)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
d->m_parenthesesMatchingEnabled = b;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::isParenthesesMatchingEnabled() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
return d->m_parenthesesMatchingEnabled;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setHighlightCurrentLine(bool b)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
d->m_highlightCurrentLine = b;
|
2014-07-25 16:23:50 +02:00
|
|
|
d->updateCurrentLineHighlight();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::highlightCurrentLine() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
return d->m_highlightCurrentLine;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setLineNumbersVisible(bool b)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
d->m_lineNumbersVisible = b;
|
2014-08-01 15:06:28 +02:00
|
|
|
d->slotUpdateExtraAreaWidth();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::lineNumbersVisible() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
return d->m_lineNumbersVisible;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setAlwaysOpenLinksInNextSplit(bool b)
|
2012-11-01 03:43:28 +01:00
|
|
|
{
|
|
|
|
|
d->m_displaySettings.m_openLinksInNextSplit = b;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::alwaysOpenLinksInNextSplit() const
|
2012-11-01 03:43:28 +01:00
|
|
|
{
|
|
|
|
|
return d->m_displaySettings.m_openLinksInNextSplit;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setMarksVisible(bool b)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
d->m_marksVisible = b;
|
2014-08-01 15:06:28 +02:00
|
|
|
d->slotUpdateExtraAreaWidth();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::marksVisible() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
return d->m_marksVisible;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setRequestMarkEnabled(bool b)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
d->m_requestMarkEnabled = b;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::requestMarkEnabled() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
return d->m_requestMarkEnabled;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setLineSeparatorsAllowed(bool b)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
d->m_lineSeparatorsAllowed = b;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::lineSeparatorsAllowed() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
return d->m_lineSeparatorsAllowed;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::updateCodeFoldingVisible()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-08-01 15:06:28 +02:00
|
|
|
const bool visible = m_codeFoldingSupported && m_displaySettings.m_displayFoldingMarkers;
|
|
|
|
|
if (m_codeFoldingVisible != visible) {
|
|
|
|
|
m_codeFoldingVisible = visible;
|
2010-11-10 15:09:18 +01:00
|
|
|
slotUpdateExtraAreaWidth();
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::reconfigure()
|
2014-09-18 13:19:51 +02:00
|
|
|
{
|
2017-03-02 12:07:11 +01:00
|
|
|
m_document->setMimeType(Utils::mimeTypeForFile(m_document->filePath().toString()).name());
|
2015-02-03 09:18:57 +01:00
|
|
|
q->configureGenericHighlighter();
|
2014-09-18 13:19:51 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::codeFoldingVisible() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
return d->m_codeFoldingVisible;
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-16 17:23:50 +01:00
|
|
|
/**
|
|
|
|
|
* Sets whether code folding is supported by the syntax highlighter. When not
|
|
|
|
|
* supported (the default), this makes sure the code folding is not shown.
|
|
|
|
|
*
|
|
|
|
|
* Needs to be called before calling setCodeFoldingVisible.
|
|
|
|
|
*/
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setCodeFoldingSupported(bool b)
|
2009-03-16 17:23:50 +01:00
|
|
|
{
|
|
|
|
|
d->m_codeFoldingSupported = b;
|
2014-08-01 15:06:28 +02:00
|
|
|
d->updateCodeFoldingVisible();
|
2009-03-16 17:23:50 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::codeFoldingSupported() const
|
2009-03-16 17:23:50 +01:00
|
|
|
{
|
|
|
|
|
return d->m_codeFoldingSupported;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setMouseNavigationEnabled(bool b)
|
2009-09-08 13:03:24 +02:00
|
|
|
{
|
2009-12-09 16:27:34 +01:00
|
|
|
d->m_behaviorSettings.m_mouseNavigation = b;
|
2009-09-08 13:03:24 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::mouseNavigationEnabled() const
|
2009-09-08 13:03:24 +02:00
|
|
|
{
|
2009-12-09 16:27:34 +01:00
|
|
|
return d->m_behaviorSettings.m_mouseNavigation;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setMouseHidingEnabled(bool b)
|
2014-02-10 13:23:59 +01:00
|
|
|
{
|
|
|
|
|
d->m_behaviorSettings.m_mouseHiding = b;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::mouseHidingEnabled() const
|
2014-02-10 13:23:59 +01:00
|
|
|
{
|
|
|
|
|
return d->m_behaviorSettings.m_mouseHiding;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setScrollWheelZoomingEnabled(bool b)
|
2009-12-09 16:27:34 +01:00
|
|
|
{
|
|
|
|
|
d->m_behaviorSettings.m_scrollWheelZooming = b;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::scrollWheelZoomingEnabled() const
|
2009-12-09 16:27:34 +01:00
|
|
|
{
|
|
|
|
|
return d->m_behaviorSettings.m_scrollWheelZooming;
|
2009-09-08 13:03:24 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setConstrainTooltips(bool b)
|
2011-07-20 13:40:24 +02:00
|
|
|
{
|
2011-12-08 13:23:41 +01:00
|
|
|
d->m_behaviorSettings.m_constrainHoverTooltips = b;
|
2011-07-20 13:40:24 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::constrainTooltips() const
|
2011-07-20 13:40:24 +02:00
|
|
|
{
|
2011-12-08 13:23:41 +01:00
|
|
|
return d->m_behaviorSettings.m_constrainHoverTooltips;
|
2011-07-20 13:40:24 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setCamelCaseNavigationEnabled(bool b)
|
2011-09-19 19:00:53 +02:00
|
|
|
{
|
|
|
|
|
d->m_behaviorSettings.m_camelCaseNavigation = b;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::camelCaseNavigationEnabled() const
|
2011-09-19 19:00:53 +02:00
|
|
|
{
|
|
|
|
|
return d->m_behaviorSettings.m_camelCaseNavigation;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setRevisionsVisible(bool b)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
d->m_revisionsVisible = b;
|
2014-08-01 15:06:28 +02:00
|
|
|
d->slotUpdateExtraAreaWidth();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::revisionsVisible() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
return d->m_revisionsVisible;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setVisibleWrapColumn(int column)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
d->m_visibleWrapColumn = column;
|
|
|
|
|
viewport()->update();
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
int TextEditorWidget::visibleWrapColumn() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
return d->m_visibleWrapColumn;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setAutoCompleter(AutoCompleter *autoCompleter)
|
2010-11-08 16:11:26 +01:00
|
|
|
{
|
|
|
|
|
d->m_autoCompleter.reset(autoCompleter);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
AutoCompleter *TextEditorWidget::autoCompleter() const
|
2010-11-08 16:11:26 +01:00
|
|
|
{
|
|
|
|
|
return d->m_autoCompleter.data();
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
//
|
|
|
|
|
// TextEditorWidgetPrivate
|
|
|
|
|
//
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::setupDocumentSignals()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-01-09 18:04:45 +01:00
|
|
|
QTextDocument *doc = m_document->document();
|
2014-08-18 17:49:04 +02:00
|
|
|
q->QPlainTextEdit::setDocument(doc);
|
2010-09-16 17:26:45 +02:00
|
|
|
q->setCursorWidth(2); // Applies to the document layout
|
|
|
|
|
|
2014-09-26 09:14:03 +02:00
|
|
|
TextDocumentLayout *documentLayout = qobject_cast<TextDocumentLayout*>(doc->documentLayout());
|
2013-12-13 09:49:29 +01:00
|
|
|
QTC_CHECK(documentLayout);
|
2014-08-01 15:06:28 +02:00
|
|
|
|
|
|
|
|
QObject::connect(documentLayout, &QPlainTextDocumentLayout::updateBlock,
|
2014-09-26 11:37:54 +02:00
|
|
|
this, &TextEditorWidgetPrivate::slotUpdateBlockNotify);
|
2014-08-01 15:06:28 +02:00
|
|
|
|
2014-09-26 09:14:03 +02:00
|
|
|
QObject::connect(documentLayout, &TextDocumentLayout::updateExtraArea,
|
2014-08-01 15:06:28 +02:00
|
|
|
m_extraArea, static_cast<void (QWidget::*)()>(&QWidget::update));
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
QObject::connect(q, &TextEditorWidget::requestBlockUpdate,
|
2014-08-01 15:06:28 +02:00
|
|
|
documentLayout, &QPlainTextDocumentLayout::updateBlock);
|
|
|
|
|
|
2015-06-04 10:43:34 +02:00
|
|
|
QObject::connect(documentLayout, &TextDocumentLayout::updateExtraArea,
|
|
|
|
|
this, &TextEditorWidgetPrivate::scheduleUpdateHighlightScrollBar);
|
|
|
|
|
|
|
|
|
|
QObject::connect(documentLayout, &QAbstractTextDocumentLayout::documentSizeChanged,
|
|
|
|
|
this, &TextEditorWidgetPrivate::scheduleUpdateHighlightScrollBar);
|
|
|
|
|
|
|
|
|
|
QObject::connect(documentLayout, &QAbstractTextDocumentLayout::update,
|
|
|
|
|
this, &TextEditorWidgetPrivate::scheduleUpdateHighlightScrollBar);
|
|
|
|
|
|
2014-08-01 15:06:28 +02:00
|
|
|
QObject::connect(doc, &QTextDocument::contentsChange,
|
2014-09-26 11:37:54 +02:00
|
|
|
this, &TextEditorWidgetPrivate::editorContentsChange);
|
2014-08-01 15:06:28 +02:00
|
|
|
|
2014-09-22 18:43:31 +02:00
|
|
|
QObject::connect(m_document.data(), &TextDocument::aboutToReload,
|
2014-09-26 11:37:54 +02:00
|
|
|
this, &TextEditorWidgetPrivate::documentAboutToBeReloaded);
|
2014-08-01 15:06:28 +02:00
|
|
|
|
2014-09-22 18:43:31 +02:00
|
|
|
QObject::connect(m_document.data(), &TextDocument::reloadFinished,
|
2014-09-26 11:37:54 +02:00
|
|
|
this, &TextEditorWidgetPrivate::documentReloadFinished);
|
2014-08-01 15:06:28 +02:00
|
|
|
|
2014-09-22 18:43:31 +02:00
|
|
|
QObject::connect(m_document.data(), &TextDocument::tabSettingsChanged,
|
2017-07-17 12:41:37 +02:00
|
|
|
this, [this](){
|
|
|
|
|
updateTabStops();
|
|
|
|
|
m_autoCompleter->setTabSettings(m_document->tabSettings());
|
|
|
|
|
});
|
2014-08-01 15:06:28 +02:00
|
|
|
|
2014-09-22 18:43:31 +02:00
|
|
|
QObject::connect(m_document.data(), &TextDocument::fontSettingsChanged,
|
2014-09-26 11:37:54 +02:00
|
|
|
this, &TextEditorWidgetPrivate::applyFontSettingsDelayed);
|
2014-08-01 15:06:28 +02:00
|
|
|
|
2017-10-06 14:29:13 +02:00
|
|
|
QObject::connect(m_document.data(), &TextDocument::markRemoved,
|
|
|
|
|
this, &TextEditorWidgetPrivate::markRemoved);
|
|
|
|
|
|
2014-08-01 15:06:28 +02:00
|
|
|
slotUpdateExtraAreaWidth();
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2014-08-19 12:12:45 +02:00
|
|
|
TextEditorSettings *settings = TextEditorSettings::instance();
|
|
|
|
|
|
|
|
|
|
// Connect to settings change signals
|
|
|
|
|
connect(settings, &TextEditorSettings::fontSettingsChanged,
|
2014-09-22 18:43:31 +02:00
|
|
|
m_document.data(), &TextDocument::setFontSettings);
|
2014-08-19 12:12:45 +02:00
|
|
|
connect(settings, &TextEditorSettings::typingSettingsChanged,
|
2014-09-26 11:37:54 +02:00
|
|
|
q, &TextEditorWidget::setTypingSettings);
|
2014-08-19 12:12:45 +02:00
|
|
|
connect(settings, &TextEditorSettings::storageSettingsChanged,
|
2014-09-26 11:37:54 +02:00
|
|
|
q, &TextEditorWidget::setStorageSettings);
|
2014-08-19 12:12:45 +02:00
|
|
|
connect(settings, &TextEditorSettings::behaviorSettingsChanged,
|
2014-09-26 11:37:54 +02:00
|
|
|
q, &TextEditorWidget::setBehaviorSettings);
|
2014-08-19 12:12:45 +02:00
|
|
|
connect(settings, &TextEditorSettings::marginSettingsChanged,
|
2014-09-26 11:37:54 +02:00
|
|
|
q, &TextEditorWidget::setMarginSettings);
|
2014-08-19 12:12:45 +02:00
|
|
|
connect(settings, &TextEditorSettings::displaySettingsChanged,
|
2014-09-26 11:37:54 +02:00
|
|
|
q, &TextEditorWidget::setDisplaySettings);
|
2014-08-19 12:12:45 +02:00
|
|
|
connect(settings, &TextEditorSettings::completionSettingsChanged,
|
2014-09-26 11:37:54 +02:00
|
|
|
q, &TextEditorWidget::setCompletionSettings);
|
2014-08-19 12:12:45 +02:00
|
|
|
connect(settings, &TextEditorSettings::extraEncodingSettingsChanged,
|
2014-09-26 11:37:54 +02:00
|
|
|
q, &TextEditorWidget::setExtraEncodingSettings);
|
2014-08-19 12:12:45 +02:00
|
|
|
|
|
|
|
|
// Apply current settings
|
|
|
|
|
m_document->setFontSettings(settings->fontSettings());
|
|
|
|
|
m_document->setTabSettings(settings->codeStyle()->tabSettings()); // also set through code style ???
|
|
|
|
|
q->setTypingSettings(settings->typingSettings());
|
|
|
|
|
q->setStorageSettings(settings->storageSettings());
|
|
|
|
|
q->setBehaviorSettings(settings->behaviorSettings());
|
|
|
|
|
q->setMarginSettings(settings->marginSettings());
|
|
|
|
|
q->setDisplaySettings(settings->displaySettings());
|
|
|
|
|
q->setCompletionSettings(settings->completionSettings());
|
|
|
|
|
q->setExtraEncodingSettings(settings->extraEncodingSettings());
|
2014-10-28 22:37:45 +02:00
|
|
|
q->setCodeStyle(settings->codeStyle(m_tabSettingsId));
|
2014-08-19 12:12:45 +02:00
|
|
|
}
|
2010-01-19 12:28:50 +01:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidgetPrivate::snippetCheckCursor(const QTextCursor &cursor)
|
2010-01-19 12:28:50 +01:00
|
|
|
{
|
|
|
|
|
if (!m_snippetOverlay->isVisible() || m_snippetOverlay->isEmpty())
|
2010-11-02 15:35:42 +01:00
|
|
|
return false;
|
2010-01-19 12:53:53 +01:00
|
|
|
|
|
|
|
|
QTextCursor start = cursor;
|
|
|
|
|
start.setPosition(cursor.selectionStart());
|
|
|
|
|
QTextCursor end = cursor;
|
|
|
|
|
end.setPosition(cursor.selectionEnd());
|
|
|
|
|
if (!m_snippetOverlay->hasCursorInSelection(start)
|
2010-11-29 17:25:01 +01:00
|
|
|
|| !m_snippetOverlay->hasCursorInSelection(end)
|
|
|
|
|
|| m_snippetOverlay->hasFirstSelectionBeginMoved()) {
|
2010-01-19 12:28:50 +01:00
|
|
|
m_snippetOverlay->setVisible(false);
|
2013-08-09 17:45:14 +02:00
|
|
|
m_snippetOverlay->mangle();
|
2010-01-19 12:28:50 +01:00
|
|
|
m_snippetOverlay->clear();
|
2010-11-02 15:35:42 +01:00
|
|
|
return false;
|
2010-01-19 12:28:50 +01:00
|
|
|
}
|
2010-11-02 15:35:42 +01:00
|
|
|
return true;
|
2010-01-19 12:28:50 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::snippetTabOrBacktab(bool forward)
|
2010-01-19 12:28:50 +01:00
|
|
|
{
|
|
|
|
|
if (!m_snippetOverlay->isVisible() || m_snippetOverlay->isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
QTextCursor cursor = q->textCursor();
|
|
|
|
|
OverlaySelection final;
|
|
|
|
|
if (forward) {
|
2010-11-02 15:35:42 +01:00
|
|
|
for (int i = 0; i < m_snippetOverlay->selections().count(); ++i){
|
|
|
|
|
const OverlaySelection &selection = m_snippetOverlay->selections().at(i);
|
2010-01-19 18:50:50 +01:00
|
|
|
if (selection.m_cursor_begin.position() >= cursor.position()
|
|
|
|
|
&& selection.m_cursor_end.position() > cursor.position()) {
|
2010-01-19 12:28:50 +01:00
|
|
|
final = selection;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2010-11-02 15:35:42 +01:00
|
|
|
for (int i = m_snippetOverlay->selections().count()-1; i >= 0; --i){
|
|
|
|
|
const OverlaySelection &selection = m_snippetOverlay->selections().at(i);
|
2010-01-19 12:28:50 +01:00
|
|
|
if (selection.m_cursor_end.position() < cursor.position()) {
|
|
|
|
|
final = selection;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
if (final.m_cursor_begin.isNull())
|
2010-11-02 15:35:42 +01:00
|
|
|
final = forward ? m_snippetOverlay->selections().first() : m_snippetOverlay->selections().last();
|
2010-01-19 12:28:50 +01:00
|
|
|
|
|
|
|
|
if (final.m_cursor_begin.position() == final.m_cursor_end.position()) { // empty tab stop
|
|
|
|
|
cursor.setPosition(final.m_cursor_end.position());
|
|
|
|
|
} else {
|
2010-08-24 12:28:51 +02:00
|
|
|
cursor.setPosition(final.m_cursor_begin.position());
|
2010-01-19 12:28:50 +01:00
|
|
|
cursor.setPosition(final.m_cursor_end.position(), QTextCursor::KeepAnchor);
|
|
|
|
|
}
|
|
|
|
|
q->setTextCursor(cursor);
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:45:07 +01:00
|
|
|
// Calculate global position for a tooltip considering the left extra area.
|
2014-09-26 11:37:54 +02:00
|
|
|
QPoint TextEditorWidget::toolTipPosition(const QTextCursor &c) const
|
2011-02-21 16:45:07 +01:00
|
|
|
{
|
|
|
|
|
const QPoint cursorPos = mapToGlobal(cursorRect(c).bottomRight() + QPoint(1,1));
|
2012-11-06 15:45:23 +01:00
|
|
|
return cursorPos + QPoint(d->m_extraArea->width(), HostOsInfo::isWindowsHost() ? -24 : -16);
|
2011-02-21 16:45:07 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::processTooltipRequest(const QTextCursor &c)
|
2011-12-08 13:23:41 +01:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
const QPoint toolTipPoint = q->toolTipPosition(c);
|
2011-12-08 13:23:41 +01:00
|
|
|
bool handled = false;
|
2014-09-19 16:17:05 +02:00
|
|
|
emit q->tooltipOverrideRequested(q, toolTipPoint, c.position(), &handled);
|
2014-09-30 13:08:05 +02:00
|
|
|
if (handled)
|
|
|
|
|
return;
|
2016-01-04 09:17:59 +11:00
|
|
|
|
|
|
|
|
if (m_hoverHandlers.isEmpty()) {
|
|
|
|
|
emit q->tooltipRequested(toolTipPoint, c.position());
|
2014-09-30 13:08:05 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2016-01-04 09:17:59 +11:00
|
|
|
|
2017-06-26 11:15:19 +02:00
|
|
|
m_hoverHandlerRunner.startChecking(c, toolTipPoint);
|
2011-12-08 13:23:41 +01:00
|
|
|
}
|
|
|
|
|
|
2017-06-20 08:28:10 +02:00
|
|
|
bool TextEditorWidgetPrivate::processAnnotaionTooltipRequest(const QTextBlock &block,
|
|
|
|
|
const QPoint &pos) const
|
|
|
|
|
{
|
|
|
|
|
TextBlockUserData *blockUserData = TextDocumentLayout::testUserData(block);
|
|
|
|
|
if (!blockUserData)
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
for (const AnnotationRect &annotationRect : m_annotationRects[block.blockNumber()]) {
|
2017-07-21 14:31:21 +02:00
|
|
|
if (!annotationRect.rect.contains(pos))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
auto layout = new QGridLayout;
|
|
|
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
|
|
|
layout->setSpacing(2);
|
|
|
|
|
annotationRect.mark->addToToolTipLayout(layout);
|
|
|
|
|
TextMarks marks = blockUserData->marks();
|
|
|
|
|
if (marks.size() > 1) {
|
|
|
|
|
QFrame* separator = new QFrame();
|
|
|
|
|
separator->setFrameShape(QFrame::HLine);
|
|
|
|
|
layout->addWidget(separator, layout->rowCount(), 0, 1, -1);
|
|
|
|
|
layout->addWidget(new QLabel(tr("Other annotations:")), layout->rowCount(), 0, 1, -1);
|
|
|
|
|
|
|
|
|
|
Utils::sort(marks, [](const TextMark* mark1, const TextMark* mark2){
|
|
|
|
|
return mark1->priority() > mark2->priority();
|
|
|
|
|
});
|
|
|
|
|
for (const TextMark *mark : Utils::asConst(marks)) {
|
|
|
|
|
if (mark != annotationRect.mark)
|
|
|
|
|
mark->addToToolTipLayout(layout);
|
2017-06-20 08:28:10 +02:00
|
|
|
}
|
|
|
|
|
}
|
2017-07-21 14:31:21 +02:00
|
|
|
layout->addWidget(DisplaySettings::createAnnotationSettingsLink(),
|
|
|
|
|
layout->rowCount(), 0, 1, -1, Qt::AlignRight);
|
|
|
|
|
ToolTip::show(q->mapToGlobal(pos), layout, q);
|
|
|
|
|
return true;
|
2017-06-20 08:28:10 +02:00
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::viewportEvent(QEvent *event)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-03-19 14:18:26 +01:00
|
|
|
d->m_contentsChanged = false;
|
2012-02-08 13:09:04 +01:00
|
|
|
if (event->type() == QEvent::ToolTip) {
|
2011-07-20 13:40:24 +02:00
|
|
|
if (QApplication::keyboardModifiers() & Qt::ControlModifier
|
|
|
|
|
|| (!(QApplication::keyboardModifiers() & Qt::ShiftModifier)
|
2011-12-08 13:23:41 +01:00
|
|
|
&& d->m_behaviorSettings.m_constrainHoverTooltips)) {
|
2011-07-20 13:40:24 +02:00
|
|
|
// Tooltips should be eaten when either control is pressed (so they don't get in the
|
|
|
|
|
// way of code navigation) or if they are in constrained mode and shift is not pressed.
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
const QHelpEvent *he = static_cast<QHelpEvent*>(event);
|
|
|
|
|
const QPoint &pos = he->pos();
|
|
|
|
|
|
2010-07-02 13:47:14 +02:00
|
|
|
RefactorMarker refactorMarker = d->m_refactorOverlay->markerAt(pos);
|
|
|
|
|
if (refactorMarker.isValid() && !refactorMarker.tooltip.isEmpty()) {
|
2014-12-08 10:57:05 +01:00
|
|
|
ToolTip::show(he->globalPos(), refactorMarker.tooltip,
|
2015-08-07 17:21:38 +02:00
|
|
|
viewport(), QString(), refactorMarker.rect);
|
2010-07-02 13:47:14 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-05-30 15:26:13 +02:00
|
|
|
QTextCursor tc = cursorForPosition(pos);
|
|
|
|
|
QTextBlock block = tc.block();
|
|
|
|
|
QTextLine line = block.layout()->lineForTextPosition(tc.positionInBlock());
|
|
|
|
|
QTC_CHECK(line.isValid());
|
|
|
|
|
// Only handle tool tip for text cursor if mouse is within the block for the text cursor,
|
|
|
|
|
// and not if the mouse is e.g. in the empty space behind a short line.
|
2017-06-20 08:28:10 +02:00
|
|
|
if (line.isValid()) {
|
|
|
|
|
if (pos.x() <= blockBoundingGeometry(block).left() + line.naturalTextRect().right()) {
|
|
|
|
|
d->processTooltipRequest(tc);
|
|
|
|
|
return true;
|
|
|
|
|
} else if (d->processAnnotaionTooltipRequest(block, pos)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
ToolTip::hide();
|
2014-05-30 15:26:13 +02:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
return QPlainTextEdit::viewportEvent(event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::resizeEvent(QResizeEvent *e)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
QPlainTextEdit::resizeEvent(e);
|
2009-11-25 15:55:45 +01:00
|
|
|
QRect cr = rect();
|
2008-12-02 12:01:29 +01:00
|
|
|
d->m_extraArea->setGeometry(
|
|
|
|
|
QStyle::visualRect(layoutDirection(), cr,
|
2017-09-05 12:01:13 +02:00
|
|
|
QRect(cr.left() + frameWidth(), cr.top() + frameWidth(),
|
|
|
|
|
extraAreaWidth(), cr.height() - 2 * frameWidth())));
|
2015-06-04 10:43:34 +02:00
|
|
|
d->adjustScrollBarRanges();
|
|
|
|
|
d->updateCurrentLineInScrollbar();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
QRect TextEditorWidgetPrivate::foldBox()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
if (m_highlightBlocksInfo.isEmpty() || extraAreaHighlightFoldedBlockNumber < 0)
|
2009-04-24 16:44:48 +02:00
|
|
|
return QRect();
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
QTextBlock begin = q->document()->findBlockByNumber(m_highlightBlocksInfo.open.last());
|
2009-04-29 15:37:38 +02:00
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
QTextBlock end = q->document()->findBlockByNumber(m_highlightBlocksInfo.close.first());
|
2009-04-24 16:44:48 +02:00
|
|
|
if (!begin.isValid() || !end.isValid())
|
|
|
|
|
return QRect();
|
2014-07-25 16:23:50 +02:00
|
|
|
QRectF br = q->blockBoundingGeometry(begin).translated(q->contentOffset());
|
|
|
|
|
QRectF er = q->blockBoundingGeometry(end).translated(q->contentOffset());
|
2009-06-05 16:16:03 +02:00
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
return QRect(m_extraArea->width() - foldBoxWidth(q->fontMetrics()),
|
2009-04-24 16:44:48 +02:00
|
|
|
int(br.top()),
|
2014-07-25 16:23:50 +02:00
|
|
|
foldBoxWidth(q->fontMetrics()),
|
2009-04-24 16:44:48 +02:00
|
|
|
er.bottom() - br.top());
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
QTextBlock TextEditorWidgetPrivate::foldedBlockAt(const QPoint &pos, QRect *box) const
|
2010-04-26 14:02:09 +02:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
QPointF offset = q->contentOffset();
|
|
|
|
|
QTextBlock block = q->firstVisibleBlock();
|
|
|
|
|
qreal top = q->blockBoundingGeometry(block).translated(offset).top();
|
|
|
|
|
qreal bottom = top + q->blockBoundingRect(block).height();
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
int viewportHeight = q->viewport()->height();
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
while (block.isValid() && top <= viewportHeight) {
|
|
|
|
|
QTextBlock nextBlock = block.next();
|
2014-07-25 16:23:50 +02:00
|
|
|
if (block.isVisible() && bottom >= 0 && q->replacementVisible(block.blockNumber())) {
|
2008-12-02 12:01:29 +01:00
|
|
|
if (nextBlock.isValid() && !nextBlock.isVisible()) {
|
|
|
|
|
QTextLayout *layout = block.layout();
|
|
|
|
|
QTextLine line = layout->lineAt(layout->lineCount()-1);
|
|
|
|
|
QRectF lineRect = line.naturalTextRect().translated(offset.x(), top);
|
|
|
|
|
lineRect.adjust(0, 0, -1, -1);
|
|
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
QString replacement = QLatin1String(" {") + q->foldReplacementText(block)
|
2011-09-14 22:16:28 +02:00
|
|
|
+ QLatin1String("}; ");
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
QRectF collapseRect(lineRect.right() + 12,
|
|
|
|
|
lineRect.top(),
|
2014-07-25 16:23:50 +02:00
|
|
|
q->fontMetrics().width(replacement),
|
2008-12-02 12:01:29 +01:00
|
|
|
lineRect.height());
|
|
|
|
|
if (collapseRect.contains(pos)) {
|
|
|
|
|
QTextBlock result = block;
|
|
|
|
|
if (box)
|
|
|
|
|
*box = collapseRect.toAlignedRect();
|
|
|
|
|
return result;
|
|
|
|
|
} else {
|
|
|
|
|
block = nextBlock;
|
|
|
|
|
while (nextBlock.isValid() && !nextBlock.isVisible()) {
|
|
|
|
|
block = nextBlock;
|
|
|
|
|
nextBlock = block.next();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
block = nextBlock;
|
|
|
|
|
top = bottom;
|
2014-07-25 16:23:50 +02:00
|
|
|
bottom = top + q->blockBoundingRect(block).height();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
return QTextBlock();
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::highlightSearchResults(const QTextBlock &block,
|
2009-12-02 10:57:05 +01:00
|
|
|
TextEditorOverlay *overlay)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
if (m_searchExpr.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
2009-12-01 19:44:31 +01:00
|
|
|
int blockPosition = block.position();
|
|
|
|
|
|
|
|
|
|
QTextCursor cursor = q->textCursor();
|
2008-12-02 12:01:29 +01:00
|
|
|
QString text = block.text();
|
|
|
|
|
text.replace(QChar::Nbsp, QLatin1Char(' '));
|
|
|
|
|
int idx = -1;
|
2009-12-02 13:31:17 +01:00
|
|
|
int l = 1;
|
2010-04-12 20:49:40 +02:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
while (idx < text.length()) {
|
2009-12-02 13:31:17 +01:00
|
|
|
idx = m_searchExpr.indexIn(text, idx + l);
|
2008-12-02 12:01:29 +01:00
|
|
|
if (idx < 0)
|
|
|
|
|
break;
|
2009-12-02 13:31:17 +01:00
|
|
|
l = m_searchExpr.matchedLength();
|
2010-04-26 19:37:19 +02:00
|
|
|
if (l == 0)
|
|
|
|
|
break;
|
2014-08-19 14:23:03 +02:00
|
|
|
if ((m_findFlags & FindWholeWords)
|
2008-12-02 12:01:29 +01:00
|
|
|
&& ((idx && text.at(idx-1).isLetterOrNumber())
|
|
|
|
|
|| (idx + l < text.length() && text.at(idx + l).isLetterOrNumber())))
|
|
|
|
|
continue;
|
|
|
|
|
|
2015-04-21 12:39:01 +02:00
|
|
|
if (!q->inFindScope(blockPosition + idx, blockPosition + idx + l))
|
2010-08-05 15:01:20 +02:00
|
|
|
continue;
|
2009-12-01 14:47:10 +01:00
|
|
|
|
2014-01-21 14:29:25 +01:00
|
|
|
const QTextCharFormat &searchResultFormat
|
|
|
|
|
= m_document->fontSettings().toTextCharFormat(C_SEARCH_RESULT);
|
2010-04-12 20:49:40 +02:00
|
|
|
overlay->addOverlaySelection(blockPosition + idx,
|
|
|
|
|
blockPosition + idx + l,
|
2014-01-21 14:29:25 +01:00
|
|
|
searchResultFormat.background().color().darker(120),
|
2010-04-12 20:49:40 +02:00
|
|
|
QColor(),
|
|
|
|
|
(idx == cursor.selectionStart() - blockPosition
|
|
|
|
|
&& idx + l == cursor.selectionEnd() - blockPosition)?
|
|
|
|
|
TextEditorOverlay::DropShadow : 0);
|
2009-12-01 14:47:10 +01:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
QString TextEditorWidgetPrivate::copyBlockSelection()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-08-05 15:01:20 +02:00
|
|
|
if (!m_inBlockSelectionMode)
|
2014-06-16 14:20:36 +02:00
|
|
|
return QString();
|
|
|
|
|
QString selection;
|
2014-01-17 15:54:59 +01:00
|
|
|
const TabSettings &ts = m_document->tabSettings();
|
2014-06-16 14:20:36 +02:00
|
|
|
QTextBlock block =
|
|
|
|
|
m_document->document()->findBlockByNumber(m_blockSelection.firstBlockNumber());
|
|
|
|
|
const QTextBlock &lastBlock =
|
|
|
|
|
m_document->document()->findBlockByNumber(m_blockSelection.lastBlockNumber());
|
2013-04-23 09:09:20 +02:00
|
|
|
bool textInserted = false;
|
2008-12-02 12:01:29 +01:00
|
|
|
for (;;) {
|
2013-04-23 09:09:20 +02:00
|
|
|
if (q->selectionVisible(block.blockNumber())) {
|
|
|
|
|
if (textInserted)
|
|
|
|
|
selection += QLatin1Char('\n');
|
|
|
|
|
textInserted = true;
|
|
|
|
|
|
|
|
|
|
QString text = block.text();
|
|
|
|
|
int startOffset = 0;
|
2014-06-16 14:20:36 +02:00
|
|
|
int startPos = ts.positionAtColumn(text, m_blockSelection.firstVisualColumn(), &startOffset);
|
2013-04-23 09:09:20 +02:00
|
|
|
int endOffset = 0;
|
2014-06-16 14:20:36 +02:00
|
|
|
int endPos = ts.positionAtColumn(text, m_blockSelection.lastVisualColumn(), &endOffset);
|
2013-04-23 09:09:20 +02:00
|
|
|
|
|
|
|
|
if (startPos == endPos) {
|
|
|
|
|
selection += QString(endOffset - startOffset, QLatin1Char(' '));
|
|
|
|
|
} else {
|
|
|
|
|
if (startOffset < 0)
|
|
|
|
|
selection += QString(-startOffset, QLatin1Char(' '));
|
|
|
|
|
if (endOffset < 0)
|
|
|
|
|
--endPos;
|
2017-04-14 10:24:28 +02:00
|
|
|
selection += text.midRef(startPos, endPos - startPos);
|
2013-04-23 09:09:20 +02:00
|
|
|
if (endOffset < 0)
|
|
|
|
|
selection += QString(ts.m_tabSize + endOffset, QLatin1Char(' '));
|
|
|
|
|
else if (endOffset > 0)
|
|
|
|
|
selection += QString(endOffset, QLatin1Char(' '));
|
|
|
|
|
}
|
2010-08-05 15:01:20 +02:00
|
|
|
}
|
|
|
|
|
if (block == lastBlock)
|
2008-12-02 12:01:29 +01:00
|
|
|
break;
|
2013-04-23 09:09:20 +02:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
block = block.next();
|
|
|
|
|
}
|
2010-08-05 15:01:20 +02:00
|
|
|
return selection;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::setCursorToColumn(QTextCursor &cursor, int column, QTextCursor::MoveMode moveMode)
|
2014-06-16 14:20:36 +02:00
|
|
|
{
|
|
|
|
|
const TabSettings &ts = m_document->tabSettings();
|
|
|
|
|
int offset = 0;
|
|
|
|
|
const int cursorPosition = cursor.position();
|
|
|
|
|
const int pos = ts.positionAtColumn(cursor.block().text(), column, &offset);
|
|
|
|
|
cursor.setPosition(cursor.block().position() + pos, offset == 0 ? moveMode : QTextCursor::MoveAnchor);
|
|
|
|
|
if (offset == 0)
|
|
|
|
|
return;
|
|
|
|
|
if (offset < 0) {
|
|
|
|
|
// the column is inside a tab so it is replaced with spaces
|
|
|
|
|
cursor.setPosition(cursor.block().position() + pos - 1, QTextCursor::KeepAnchor);
|
|
|
|
|
cursor.insertText(ts.indentationString(
|
|
|
|
|
ts.columnAt(cursor.block().text(), pos - 1),
|
2016-01-13 14:32:23 +01:00
|
|
|
ts.columnAt(cursor.block().text(), pos), 0, cursor.block()));
|
2014-06-16 14:20:36 +02:00
|
|
|
} else {
|
|
|
|
|
// column is behind the last position
|
|
|
|
|
cursor.insertText(ts.indentationString(ts.columnAt(cursor.block().text(), pos),
|
2016-01-13 14:32:23 +01:00
|
|
|
column, 0, cursor.block()));
|
2014-06-16 14:20:36 +02:00
|
|
|
}
|
|
|
|
|
if (moveMode == QTextCursor::KeepAnchor)
|
|
|
|
|
cursor.setPosition(cursorPosition);
|
|
|
|
|
cursor.setPosition(cursor.block().position() + ts.positionAtColumn(
|
|
|
|
|
cursor.block().text(), column), moveMode);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::insertIntoBlockSelection(const QString &text)
|
2014-06-16 14:20:36 +02:00
|
|
|
{
|
|
|
|
|
// TODO: add autocompleter support
|
|
|
|
|
QTextCursor cursor = q->textCursor();
|
|
|
|
|
cursor.beginEditBlock();
|
|
|
|
|
|
|
|
|
|
if (q->overwriteMode() && m_blockSelection.lastVisualColumn() == m_blockSelection.positionColumn)
|
|
|
|
|
++m_blockSelection.positionColumn;
|
|
|
|
|
|
|
|
|
|
if (m_blockSelection.positionColumn != m_blockSelection.anchorColumn) {
|
|
|
|
|
removeBlockSelection();
|
|
|
|
|
if (!m_inBlockSelectionMode) {
|
|
|
|
|
q->insertPlainText(text);
|
|
|
|
|
cursor.endEditBlock();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (text.isEmpty()) {
|
|
|
|
|
cursor.endEditBlock();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int positionBlock = m_blockSelection.positionBlock;
|
|
|
|
|
int anchorBlock = m_blockSelection.anchorBlock;
|
|
|
|
|
int column = m_blockSelection.positionColumn;
|
|
|
|
|
|
|
|
|
|
const QTextBlock &firstBlock =
|
|
|
|
|
m_document->document()->findBlockByNumber(m_blockSelection.firstBlockNumber());
|
|
|
|
|
QTextBlock block =
|
|
|
|
|
m_document->document()->findBlockByNumber(m_blockSelection.lastBlockNumber());
|
|
|
|
|
|
|
|
|
|
// unify the length of all lines in a multiline text
|
|
|
|
|
const int selectionLineCount = m_blockSelection.lastBlockNumber()
|
|
|
|
|
- m_blockSelection.firstBlockNumber();
|
|
|
|
|
const int textNewLineCount = text.count(QLatin1Char('\n')) ;
|
|
|
|
|
QStringList textLines = text.split(QLatin1Char('\n'));
|
|
|
|
|
const TabSettings &ts = m_document->tabSettings();
|
|
|
|
|
int textLength = 0;
|
|
|
|
|
const QStringList::const_iterator endLine = textLines.constEnd();
|
|
|
|
|
for (QStringList::const_iterator textLine = textLines.constBegin(); textLine != endLine; ++textLine)
|
|
|
|
|
textLength += qMax(0, ts.columnCountForText(*textLine, column) - textLength);
|
2015-04-01 11:19:32 +02:00
|
|
|
for (QStringList::iterator textLine = textLines.begin(); textLine != textLines.end(); ++textLine)
|
2014-06-16 14:20:36 +02:00
|
|
|
textLine->append(QString(qMax(0, textLength - ts.columnCountForText(*textLine, column)), QLatin1Char(' ')));
|
|
|
|
|
|
|
|
|
|
// insert Text
|
|
|
|
|
for (;;) {
|
|
|
|
|
// If the number of lines to be inserted equals the number of the selected lines the
|
|
|
|
|
// lines of the copy paste buffer are inserted in the corresponding lines of the selection.
|
|
|
|
|
// Otherwise the complete buffer is inserted in each of the selected lines.
|
|
|
|
|
cursor.setPosition(block.position());
|
|
|
|
|
if (selectionLineCount == textNewLineCount) {
|
|
|
|
|
setCursorToColumn(cursor, column);
|
|
|
|
|
cursor.insertText(textLines.at(block.blockNumber()
|
|
|
|
|
- m_blockSelection.firstBlockNumber()));
|
|
|
|
|
} else {
|
|
|
|
|
QStringList::const_iterator textLine = textLines.constBegin();
|
|
|
|
|
while (true) {
|
|
|
|
|
setCursorToColumn(cursor, column);
|
|
|
|
|
cursor.insertText(*textLine);
|
|
|
|
|
++textLine;
|
|
|
|
|
if (textLine == endLine)
|
|
|
|
|
break;
|
|
|
|
|
cursor.movePosition(QTextCursor::EndOfBlock);
|
|
|
|
|
cursor.insertText(QLatin1String("\n"));
|
|
|
|
|
if (qMax(anchorBlock, positionBlock) == anchorBlock)
|
|
|
|
|
++anchorBlock;
|
|
|
|
|
else
|
|
|
|
|
++positionBlock;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (block == firstBlock)
|
|
|
|
|
break;
|
|
|
|
|
block = block.previous();
|
|
|
|
|
}
|
|
|
|
|
cursor.endEditBlock();
|
|
|
|
|
|
|
|
|
|
column += textLength;
|
|
|
|
|
m_blockSelection.fromPostition(positionBlock, column, anchorBlock, column);
|
2015-04-21 12:36:11 +02:00
|
|
|
q->doSetTextCursor(m_blockSelection.selection(m_document.data()), true);
|
2014-06-16 14:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::removeBlockSelection()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
QTextCursor cursor = q->textCursor();
|
2010-08-05 15:01:20 +02:00
|
|
|
if (!cursor.hasSelection() || !m_inBlockSelectionMode)
|
2008-12-02 12:01:29 +01:00
|
|
|
return;
|
|
|
|
|
|
2014-06-16 14:20:36 +02:00
|
|
|
const int firstColumn = m_blockSelection.firstVisualColumn();
|
|
|
|
|
const int lastColumn = m_blockSelection.lastVisualColumn();
|
|
|
|
|
if (firstColumn == lastColumn)
|
|
|
|
|
return;
|
|
|
|
|
const int positionBlock = m_blockSelection.positionBlock;
|
|
|
|
|
const int anchorBlock = m_blockSelection.anchorBlock;
|
|
|
|
|
|
2010-08-05 15:01:20 +02:00
|
|
|
int cursorPosition = cursor.selectionStart();
|
2008-12-02 12:01:29 +01:00
|
|
|
cursor.clearSelection();
|
|
|
|
|
cursor.beginEditBlock();
|
|
|
|
|
|
2014-01-17 15:54:59 +01:00
|
|
|
const TabSettings &ts = m_document->tabSettings();
|
2014-06-16 14:20:36 +02:00
|
|
|
QTextBlock block = m_document->document()->findBlockByNumber(m_blockSelection.firstBlockNumber());
|
|
|
|
|
const QTextBlock &lastBlock = m_document->document()->findBlockByNumber(m_blockSelection.lastBlockNumber());
|
2008-12-02 12:01:29 +01:00
|
|
|
for (;;) {
|
2010-08-05 15:01:20 +02:00
|
|
|
int startOffset = 0;
|
2014-06-16 14:20:36 +02:00
|
|
|
const int startPos = ts.positionAtColumn(block.text(), firstColumn, &startOffset);
|
|
|
|
|
// removing stuff doesn't make sense if the cursor is behind the code
|
|
|
|
|
if (startPos < block.length() - 1 || startOffset < 0) {
|
|
|
|
|
cursor.setPosition(block.position());
|
|
|
|
|
setCursorToColumn(cursor, firstColumn);
|
|
|
|
|
setCursorToColumn(cursor, lastColumn, QTextCursor::KeepAnchor);
|
|
|
|
|
cursor.removeSelectedText();
|
|
|
|
|
}
|
2010-08-05 15:01:20 +02:00
|
|
|
if (block == lastBlock)
|
2008-12-02 12:01:29 +01:00
|
|
|
break;
|
|
|
|
|
block = block.next();
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-05 15:01:20 +02:00
|
|
|
cursor.setPosition(cursorPosition);
|
2008-12-02 12:01:29 +01:00
|
|
|
cursor.endEditBlock();
|
2014-06-16 14:20:36 +02:00
|
|
|
m_blockSelection.fromPostition(positionBlock, firstColumn, anchorBlock, firstColumn);
|
|
|
|
|
cursor = m_blockSelection.selection(m_document.data());
|
2015-04-21 12:36:11 +02:00
|
|
|
q->doSetTextCursor(cursor, m_blockSelection.hasSelection());
|
2014-06-16 14:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::enableBlockSelection(const QTextCursor &cursor)
|
2014-06-16 14:20:36 +02:00
|
|
|
{
|
|
|
|
|
const TabSettings &ts = m_document->tabSettings();
|
|
|
|
|
const QTextBlock &positionTextBlock = cursor.block();
|
|
|
|
|
int positionBlock = positionTextBlock.blockNumber();
|
|
|
|
|
int positionColumn = ts.columnAt(positionTextBlock.text(),
|
|
|
|
|
cursor.position() - positionTextBlock.position());
|
|
|
|
|
|
|
|
|
|
const QTextDocument *document = cursor.document();
|
|
|
|
|
const QTextBlock &anchorTextBlock = document->findBlock(cursor.anchor());
|
|
|
|
|
int anchorBlock = anchorTextBlock.blockNumber();
|
|
|
|
|
int anchorColumn = ts.columnAt(anchorTextBlock.text(),
|
|
|
|
|
cursor.anchor() - anchorTextBlock.position());
|
|
|
|
|
enableBlockSelection(positionBlock, anchorColumn, anchorBlock, positionColumn);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::enableBlockSelection(int positionBlock, int positionColumn,
|
2014-06-16 14:20:36 +02:00
|
|
|
int anchorBlock, int anchorColumn)
|
|
|
|
|
{
|
|
|
|
|
m_blockSelection.fromPostition(positionBlock, anchorColumn, anchorBlock, positionColumn);
|
|
|
|
|
resetCursorFlashTimer();
|
|
|
|
|
m_inBlockSelectionMode = true;
|
2015-04-21 12:36:11 +02:00
|
|
|
q->doSetTextCursor(m_blockSelection.selection(m_document.data()), true);
|
2014-06-16 14:20:36 +02:00
|
|
|
q->viewport()->update();
|
|
|
|
|
}
|
|
|
|
|
|
2016-12-21 08:44:10 +01:00
|
|
|
void TextEditorWidgetPrivate::disableBlockSelection(BlockSelectionUpdateKind kind)
|
2014-06-16 14:20:36 +02:00
|
|
|
{
|
|
|
|
|
m_inBlockSelectionMode = false;
|
|
|
|
|
m_cursorFlashTimer.stop();
|
2016-12-21 08:44:10 +01:00
|
|
|
if (kind != NoCursorUpdate) {
|
|
|
|
|
QTextCursor cursor = m_blockSelection.selection(m_document.data());
|
|
|
|
|
if (kind == CursorUpdateClearSelection)
|
|
|
|
|
cursor.clearSelection();
|
|
|
|
|
q->setTextCursor(cursor);
|
|
|
|
|
}
|
2014-06-16 14:20:36 +02:00
|
|
|
m_blockSelection.clear();
|
|
|
|
|
q->viewport()->update();
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::resetCursorFlashTimer()
|
2014-06-16 14:20:36 +02:00
|
|
|
{
|
|
|
|
|
m_cursorVisible = true;
|
2017-04-24 17:01:10 +02:00
|
|
|
const int flashTime = QApplication::cursorFlashTime();
|
2014-06-16 14:20:36 +02:00
|
|
|
if (flashTime > 0) {
|
|
|
|
|
m_cursorFlashTimer.stop();
|
|
|
|
|
m_cursorFlashTimer.start(flashTime / 2, q);
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::moveCursorVisible(bool ensureVisible)
|
2009-03-17 16:16:36 +01:00
|
|
|
{
|
|
|
|
|
QTextCursor cursor = q->textCursor();
|
|
|
|
|
if (!cursor.block().isVisible()) {
|
|
|
|
|
cursor.setVisualNavigation(true);
|
|
|
|
|
cursor.movePosition(QTextCursor::Up);
|
|
|
|
|
q->setTextCursor(cursor);
|
|
|
|
|
}
|
|
|
|
|
if (ensureVisible)
|
|
|
|
|
q->ensureCursorVisible();
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-25 13:46:08 +01:00
|
|
|
static QColor blendColors(const QColor &a, const QColor &b, int alpha)
|
|
|
|
|
{
|
|
|
|
|
return QColor((a.red() * (256 - alpha) + b.red() * alpha) / 256,
|
|
|
|
|
(a.green() * (256 - alpha) + b.green() * alpha) / 256,
|
|
|
|
|
(a.blue() * (256 - alpha) + b.blue() * alpha) / 256);
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-28 11:43:13 +02:00
|
|
|
static QColor calcBlendColor(const QColor &baseColor, int level, int count)
|
|
|
|
|
{
|
|
|
|
|
QColor color80;
|
|
|
|
|
QColor color90;
|
|
|
|
|
|
|
|
|
|
if (baseColor.value() > 128) {
|
2009-07-20 18:22:55 +02:00
|
|
|
const int f90 = 15;
|
|
|
|
|
const int f80 = 30;
|
|
|
|
|
color80.setRgb(qMax(0, baseColor.red() - f80),
|
|
|
|
|
qMax(0, baseColor.green() - f80),
|
|
|
|
|
qMax(0, baseColor.blue() - f80));
|
|
|
|
|
color90.setRgb(qMax(0, baseColor.red() - f90),
|
|
|
|
|
qMax(0, baseColor.green() - f90),
|
|
|
|
|
qMax(0, baseColor.blue() - f90));
|
2009-04-28 11:43:13 +02:00
|
|
|
} else {
|
2009-07-20 18:22:55 +02:00
|
|
|
const int f90 = 20;
|
|
|
|
|
const int f80 = 40;
|
|
|
|
|
color80.setRgb(qMin(255, baseColor.red() + f80),
|
|
|
|
|
qMin(255, baseColor.green() + f80),
|
|
|
|
|
qMin(255, baseColor.blue() + f80));
|
|
|
|
|
color90.setRgb(qMin(255, baseColor.red() + f90),
|
|
|
|
|
qMin(255, baseColor.green() + f90),
|
|
|
|
|
qMin(255, baseColor.blue() + f90));
|
2009-04-28 11:43:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (level == count)
|
|
|
|
|
return baseColor;
|
|
|
|
|
if (level == 0)
|
|
|
|
|
return color80;
|
|
|
|
|
if (level == count - 1)
|
|
|
|
|
return color90;
|
|
|
|
|
|
|
|
|
|
const int blendFactor = level * (256 / (count - 2));
|
|
|
|
|
|
2010-01-25 13:46:08 +01:00
|
|
|
return blendColors(color80, color90, blendFactor);
|
2009-04-28 11:43:13 +02:00
|
|
|
}
|
|
|
|
|
|
2014-06-16 14:20:36 +02:00
|
|
|
static QTextLayout::FormatRange createBlockCursorCharFormatRange(int pos, const QPalette &palette)
|
|
|
|
|
{
|
|
|
|
|
QTextLayout::FormatRange o;
|
|
|
|
|
o.start = pos;
|
|
|
|
|
o.length = 1;
|
|
|
|
|
o.format.setForeground(palette.base());
|
|
|
|
|
o.format.setBackground(palette.text());
|
|
|
|
|
return o;
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-19 14:51:21 +02:00
|
|
|
static TextMarks availableMarks(const TextMarks &marks,
|
|
|
|
|
QRectF &boundingRect,
|
|
|
|
|
const QFontMetrics &fm,
|
|
|
|
|
const qreal itemOffset)
|
|
|
|
|
{
|
|
|
|
|
TextMarks ret;
|
|
|
|
|
bool first = true;
|
|
|
|
|
for (TextMark *mark : marks) {
|
|
|
|
|
const TextMark::AnnotationRects &rects = mark->annotationRects(
|
|
|
|
|
boundingRect, fm, first ? 0 : itemOffset, 0);
|
|
|
|
|
if (rects.annotationRect.isEmpty())
|
|
|
|
|
break;
|
|
|
|
|
boundingRect.setLeft(rects.fadeOutRect.right());
|
|
|
|
|
ret.append(mark);
|
|
|
|
|
if (boundingRect.isEmpty())
|
|
|
|
|
break;
|
|
|
|
|
first = false;
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QRectF TextEditorWidgetPrivate::getLastLineLineRect(const QTextBlock &block)
|
|
|
|
|
{
|
|
|
|
|
const QTextLayout *layout = block.layout();
|
|
|
|
|
const int lineCount = layout->lineCount();
|
|
|
|
|
if (lineCount < 1)
|
|
|
|
|
return QRectF();
|
|
|
|
|
const QTextLine line = layout->lineAt(lineCount - 1);
|
|
|
|
|
const QPointF contentOffset = q->contentOffset();
|
|
|
|
|
const qreal top = q->blockBoundingGeometry(block).translated(contentOffset).top();
|
|
|
|
|
return line.naturalTextRect().translated(contentOffset.x(), top).adjusted(0, 0, -1, -1);
|
|
|
|
|
}
|
|
|
|
|
|
2017-10-17 08:23:41 +02:00
|
|
|
void TextEditorWidgetPrivate::updateLineAnnotation(
|
2017-10-05 10:03:25 +02:00
|
|
|
QPainter &painter, const QTextBlock &block, qreal rightMargin, const QRect &eventRect)
|
2017-06-20 08:28:10 +02:00
|
|
|
{
|
2017-10-17 08:23:41 +02:00
|
|
|
m_annotationRects.remove(block.blockNumber());
|
|
|
|
|
|
2017-06-20 08:28:10 +02:00
|
|
|
if (!m_displaySettings.m_displayAnnotations)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
TextBlockUserData *blockUserData = TextDocumentLayout::testUserData(block);
|
|
|
|
|
if (!blockUserData)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
TextMarks marks = blockUserData->marks();
|
|
|
|
|
if (marks.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
2017-07-19 14:51:21 +02:00
|
|
|
const QRectF lineRect = getLastLineLineRect(block);
|
|
|
|
|
if (lineRect.isNull())
|
2017-06-20 08:28:10 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Utils::sort(marks, [](const TextMark* mark1, const TextMark* mark2){
|
|
|
|
|
return mark1->priority() > mark2->priority();
|
|
|
|
|
});
|
|
|
|
|
|
2017-07-05 09:03:46 +02:00
|
|
|
const qreal itemOffset = q->fontMetrics().lineSpacing();
|
2017-07-19 14:51:21 +02:00
|
|
|
const qreal initialOffset = itemOffset * 2;
|
|
|
|
|
const qreal minimalContentWidth = q->fontMetrics().width('X')
|
|
|
|
|
* m_displaySettings.m_minimalAnnotationContent;
|
|
|
|
|
QRectF boundingRect(lineRect.topLeft().x(), lineRect.topLeft().y(),
|
|
|
|
|
q->viewport()->width() - lineRect.right(), lineRect.height());
|
|
|
|
|
qreal offset = initialOffset;
|
|
|
|
|
if (marks.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
if (m_displaySettings.m_annotationAlignment == AnnotationAlignment::NextToMargin
|
|
|
|
|
&& rightMargin > lineRect.right() + offset
|
|
|
|
|
&& q->viewport()->width() > rightMargin + minimalContentWidth) {
|
|
|
|
|
offset = rightMargin - lineRect.right();
|
|
|
|
|
} else if (m_displaySettings.m_annotationAlignment != AnnotationAlignment::NextToContent) {
|
|
|
|
|
marks = availableMarks(marks, boundingRect, q->fontMetrics(), itemOffset);
|
|
|
|
|
if (boundingRect.width() > 0)
|
|
|
|
|
offset = qMax(boundingRect.width(), initialOffset);
|
|
|
|
|
}
|
2017-06-20 08:28:10 +02:00
|
|
|
|
2017-07-19 14:51:21 +02:00
|
|
|
qreal x = lineRect.right();
|
2017-06-20 08:28:10 +02:00
|
|
|
for (const TextMark *mark : marks) {
|
2017-07-19 14:51:21 +02:00
|
|
|
boundingRect = QRectF(x, lineRect.top(), q->viewport()->width() - x, lineRect.height());
|
|
|
|
|
if (boundingRect.isEmpty())
|
2017-06-20 08:28:10 +02:00
|
|
|
break;
|
2017-10-17 08:23:41 +02:00
|
|
|
if (eventRect.intersects(boundingRect.toRect()))
|
|
|
|
|
mark->paintAnnotation(painter, &boundingRect, offset, itemOffset / 2, q->contentOffset());
|
2017-07-19 14:51:21 +02:00
|
|
|
|
|
|
|
|
x = boundingRect.right();
|
|
|
|
|
offset = itemOffset / 2;
|
|
|
|
|
m_annotationRects[block.blockNumber()].append({boundingRect, mark});
|
2017-06-20 08:28:10 +02:00
|
|
|
}
|
2017-10-05 10:03:25 +02:00
|
|
|
|
|
|
|
|
QRect updateRect(lineRect.toRect().topRight(), boundingRect.toRect().bottomRight());
|
2017-10-17 13:27:03 +02:00
|
|
|
updateRect.setLeft(qBound(0, updateRect.left(), q->viewport()->width() - 1));
|
|
|
|
|
updateRect.setRight(qBound(0, updateRect.right(), q->viewport()->width() - 1));
|
2017-10-05 10:03:25 +02:00
|
|
|
if (!updateRect.isEmpty() && !eventRect.contains(updateRect))
|
|
|
|
|
q->viewport()->update(updateRect);
|
2017-06-20 08:28:10 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::paintEvent(QPaintEvent *e)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-09-23 18:38:36 +02:00
|
|
|
// draw backgrond to the right of the wrap column before everything else
|
|
|
|
|
qreal lineX = 0;
|
|
|
|
|
QPointF offset(contentOffset());
|
2015-04-24 10:40:23 +02:00
|
|
|
const QRect &viewportRect = viewport()->rect();
|
|
|
|
|
const QRect &er = e->rect();
|
2014-09-23 18:38:36 +02:00
|
|
|
|
|
|
|
|
const FontSettings &fs = textDocument()->fontSettings();
|
|
|
|
|
const QTextCharFormat &searchScopeFormat = fs.toTextCharFormat(C_SEARCH_SCOPE);
|
|
|
|
|
const QTextCharFormat &ifdefedOutFormat = fs.toTextCharFormat(C_DISABLED_CODE);
|
|
|
|
|
|
|
|
|
|
if (d->m_visibleWrapColumn > 0) {
|
|
|
|
|
QPainter painter(viewport());
|
|
|
|
|
// Don't use QFontMetricsF::averageCharWidth here, due to it returning
|
|
|
|
|
// a fractional size even when this is not supported by the platform.
|
|
|
|
|
lineX = QFontMetricsF(font()).width(QLatin1Char('x')) * d->m_visibleWrapColumn + offset.x() + 4;
|
|
|
|
|
if (lineX < viewportRect.width())
|
|
|
|
|
painter.fillRect(QRectF(lineX, er.top(), viewportRect.width() - lineX, er.height()),
|
|
|
|
|
ifdefedOutFormat.background());
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
/*
|
|
|
|
|
Here comes an almost verbatim copy of
|
|
|
|
|
QPlainTextEdit::paintEvent() so we can adjust the extra
|
|
|
|
|
selections dynamically to indicate all search results.
|
|
|
|
|
*/
|
|
|
|
|
//begin QPlainTextEdit::paintEvent()
|
|
|
|
|
|
|
|
|
|
QPainter painter(viewport());
|
|
|
|
|
QTextDocument *doc = document();
|
2014-09-26 09:14:03 +02:00
|
|
|
TextDocumentLayout *documentLayout = qobject_cast<TextDocumentLayout*>(doc->documentLayout());
|
2008-12-09 15:25:01 +01:00
|
|
|
QTC_ASSERT(documentLayout, return);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-04-14 13:39:36 +02:00
|
|
|
QTextBlock textCursorBlock = textCursor().block();
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-05-04 13:46:50 +02:00
|
|
|
bool hasMainSelection = textCursor().hasSelection();
|
2014-01-21 14:29:25 +01:00
|
|
|
bool suppressSyntaxInIfdefedOutBlock = (ifdefedOutFormat.foreground()
|
2010-03-05 12:33:33 +01:00
|
|
|
!= palette().foreground());
|
2009-05-04 13:46:50 +02:00
|
|
|
|
2009-11-26 16:34:14 +01:00
|
|
|
// Set a brush origin so that the WaveUnderline knows where the wave started
|
|
|
|
|
painter.setBrushOrigin(offset);
|
|
|
|
|
|
2009-04-23 20:11:00 +02:00
|
|
|
// // keep right margin clean from full-width selection
|
|
|
|
|
// int maxX = offset.x() + qMax((qreal)viewportRect.width(), documentLayout->documentSize().width())
|
|
|
|
|
// - doc->documentMargin();
|
|
|
|
|
// er.setRight(qMin(er.right(), maxX));
|
|
|
|
|
// painter.setClipRect(er);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
bool editable = !isReadOnly();
|
|
|
|
|
QTextBlock block = firstVisibleBlock();
|
|
|
|
|
|
|
|
|
|
QAbstractTextDocumentLayout::PaintContext context = getPaintContext();
|
|
|
|
|
|
2014-08-08 12:58:13 +02:00
|
|
|
int documentWidth = int(document()->size().width());
|
|
|
|
|
|
2010-02-25 13:00:33 +01:00
|
|
|
if (!d->m_highlightBlocksInfo.isEmpty()) {
|
2010-05-31 13:48:40 +02:00
|
|
|
const QColor baseColor = palette().base().color();
|
|
|
|
|
|
2010-02-25 13:00:33 +01:00
|
|
|
// extra pass for the block highlight
|
|
|
|
|
|
|
|
|
|
const int margin = 5;
|
|
|
|
|
QTextBlock blockFP = block;
|
|
|
|
|
QPointF offsetFP = offset;
|
|
|
|
|
while (blockFP.isValid()) {
|
|
|
|
|
QRectF r = blockBoundingRect(blockFP).translated(offsetFP);
|
|
|
|
|
|
|
|
|
|
int n = blockFP.blockNumber();
|
|
|
|
|
int depth = 0;
|
|
|
|
|
foreach (int i, d->m_highlightBlocksInfo.open)
|
|
|
|
|
if (n >= i)
|
|
|
|
|
++depth;
|
|
|
|
|
foreach (int i, d->m_highlightBlocksInfo.close)
|
|
|
|
|
if (n > i)
|
|
|
|
|
--depth;
|
|
|
|
|
|
|
|
|
|
int count = d->m_highlightBlocksInfo.count();
|
|
|
|
|
if (count) {
|
|
|
|
|
for (int i = 0; i <= depth; ++i) {
|
2010-11-12 11:31:12 +01:00
|
|
|
const QColor &blendedColor = calcBlendColor(baseColor, i, count);
|
2010-02-25 13:00:33 +01:00
|
|
|
int vi = i > 0 ? d->m_highlightBlocksInfo.visualIndent.at(i-1) : 0;
|
2010-11-12 11:31:12 +01:00
|
|
|
QRectF oneRect = r;
|
2014-08-08 12:58:13 +02:00
|
|
|
oneRect.setWidth(qMax(viewport()->width(), documentWidth));
|
2014-08-11 17:34:52 +02:00
|
|
|
oneRect.adjust(vi, 0, 0, 0);
|
2010-11-12 11:31:12 +01:00
|
|
|
if (oneRect.left() >= oneRect.right())
|
|
|
|
|
continue;
|
|
|
|
|
if (lineX > 0 && oneRect.left() < lineX && oneRect.right() > lineX) {
|
|
|
|
|
QRectF otherRect = r;
|
|
|
|
|
otherRect.setLeft(lineX + 1);
|
|
|
|
|
otherRect.setRight(oneRect.right());
|
|
|
|
|
oneRect.setRight(lineX - 1);
|
|
|
|
|
painter.fillRect(otherRect, blendedColor);
|
|
|
|
|
}
|
|
|
|
|
painter.fillRect(oneRect, blendedColor);
|
2010-02-25 13:00:33 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
offsetFP.ry() += r.height();
|
|
|
|
|
|
|
|
|
|
if (offsetFP.y() > viewportRect.height() + margin)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
blockFP = blockFP.next();
|
|
|
|
|
if (!blockFP.isVisible()) {
|
|
|
|
|
// invisible blocks do have zero line count
|
|
|
|
|
blockFP = doc->findBlockByLineNumber(blockFP.firstLineNumber());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-05 15:01:20 +02:00
|
|
|
int blockSelectionIndex = -1;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2014-06-16 14:20:36 +02:00
|
|
|
if (d->m_inBlockSelectionMode && context.selections.count()
|
|
|
|
|
&& context.selections.last().cursor == textCursor()) {
|
2010-08-05 15:01:20 +02:00
|
|
|
blockSelectionIndex = context.selections.size()-1;
|
|
|
|
|
context.selections[blockSelectionIndex].format.clearBackground();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QTextBlock visibleCollapsedBlock;
|
|
|
|
|
QPointF visibleCollapsedBlockOffset;
|
|
|
|
|
|
2009-11-25 15:55:45 +01:00
|
|
|
QTextLayout *cursor_layout = 0;
|
|
|
|
|
QPointF cursor_offset;
|
|
|
|
|
int cursor_cpos = 0;
|
|
|
|
|
QPen cursor_pen;
|
2009-04-23 17:28:53 +02:00
|
|
|
|
2009-11-26 15:26:30 +01:00
|
|
|
d->m_searchResultOverlay->clear();
|
2009-12-02 10:57:05 +01:00
|
|
|
if (!d->m_searchExpr.isEmpty()) { // first pass for the search result overlays
|
2009-12-01 14:47:10 +01:00
|
|
|
|
2009-12-02 10:57:05 +01:00
|
|
|
const int margin = 5;
|
|
|
|
|
QTextBlock blockFP = block;
|
|
|
|
|
QPointF offsetFP = offset;
|
|
|
|
|
while (blockFP.isValid()) {
|
|
|
|
|
QRectF r = blockBoundingRect(blockFP).translated(offsetFP);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-12-02 10:57:05 +01:00
|
|
|
if (r.bottom() >= er.top() - margin && r.top() <= er.bottom() + margin) {
|
|
|
|
|
d->highlightSearchResults(blockFP,
|
|
|
|
|
d->m_searchResultOverlay);
|
|
|
|
|
}
|
|
|
|
|
offsetFP.ry() += r.height();
|
|
|
|
|
|
|
|
|
|
if (offsetFP.y() > viewportRect.height() + margin)
|
|
|
|
|
break;
|
2009-05-28 11:11:31 +02:00
|
|
|
|
2009-12-02 10:57:05 +01:00
|
|
|
blockFP = blockFP.next();
|
2010-02-25 13:00:33 +01:00
|
|
|
if (!blockFP.isVisible()) {
|
|
|
|
|
// invisible blocks do have zero line count
|
|
|
|
|
blockFP = doc->findBlockByLineNumber(blockFP.firstLineNumber());
|
|
|
|
|
}
|
2009-10-08 12:57:26 +02:00
|
|
|
}
|
|
|
|
|
|
2009-12-02 10:57:05 +01:00
|
|
|
} // end first pass
|
|
|
|
|
|
2009-12-02 13:24:53 +01:00
|
|
|
|
2010-11-02 13:47:45 +01:00
|
|
|
{ // extra pass for ifdefed out blocks
|
|
|
|
|
QTextBlock blockIDO = block;
|
|
|
|
|
QPointF offsetIDO = offset;
|
|
|
|
|
while (blockIDO.isValid()) {
|
|
|
|
|
|
|
|
|
|
QRectF r = blockBoundingRect(blockIDO).translated(offsetIDO);
|
|
|
|
|
|
|
|
|
|
if (r.bottom() >= er.top() && r.top() <= er.bottom()) {
|
2014-09-26 09:14:03 +02:00
|
|
|
if (TextDocumentLayout::ifdefedOut(blockIDO)) {
|
2010-11-02 13:47:45 +01:00
|
|
|
QRectF rr = r;
|
|
|
|
|
rr.setRight(viewportRect.width() - offset.x());
|
|
|
|
|
if (lineX > 0)
|
|
|
|
|
rr.setRight(qMin(lineX, rr.right()));
|
2014-01-21 14:29:25 +01:00
|
|
|
painter.fillRect(rr, ifdefedOutFormat.background());
|
2010-11-02 13:47:45 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
offsetIDO.ry() += r.height();
|
|
|
|
|
|
|
|
|
|
if (offsetIDO.y() > viewportRect.height())
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
blockIDO = blockIDO.next();
|
|
|
|
|
if (!blockIDO.isVisible()) {
|
|
|
|
|
// invisible blocks do have zero line count
|
|
|
|
|
blockIDO = doc->findBlockByLineNumber(blockIDO.firstLineNumber());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-01 03:10:07 -07:00
|
|
|
// draw wrap column after ifdefed out blocks
|
|
|
|
|
if (d->m_visibleWrapColumn > 0) {
|
|
|
|
|
if (lineX < viewportRect.width()) {
|
|
|
|
|
const QBrush background = ifdefedOutFormat.background();
|
|
|
|
|
const QColor col = (palette().base().color().value() > 128) ? Qt::black : Qt::white;
|
|
|
|
|
const QPen pen = painter.pen();
|
|
|
|
|
painter.setPen(blendColors(background.isOpaque() ? background.color() : palette().base().color(),
|
|
|
|
|
col, 32));
|
|
|
|
|
painter.drawLine(QPointF(lineX, er.top()), QPointF(lineX, er.bottom()));
|
|
|
|
|
painter.setPen(pen);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-05 15:01:20 +02:00
|
|
|
// possible extra pass for the block selection find scope
|
|
|
|
|
if (!d->m_findScopeStart.isNull() && d->m_findScopeVerticalBlockSelectionFirstColumn >= 0) {
|
|
|
|
|
QTextBlock blockFS = block;
|
|
|
|
|
QPointF offsetFS = offset;
|
|
|
|
|
while (blockFS.isValid()) {
|
|
|
|
|
|
|
|
|
|
QRectF r = blockBoundingRect(blockFS).translated(offsetFS);
|
|
|
|
|
|
|
|
|
|
if (r.bottom() >= er.top() && r.top() <= er.bottom()) {
|
|
|
|
|
|
|
|
|
|
if (blockFS.position() >= d->m_findScopeStart.block().position()
|
|
|
|
|
&& blockFS.position() <= d->m_findScopeEnd.block().position()) {
|
|
|
|
|
QTextLayout *layout = blockFS.layout();
|
|
|
|
|
QString text = blockFS.text();
|
2014-01-17 15:54:59 +01:00
|
|
|
const TabSettings &ts = d->m_document->tabSettings();
|
2010-08-06 16:23:13 +02:00
|
|
|
qreal spacew = QFontMetricsF(font()).width(QLatin1Char(' '));
|
2010-08-05 15:01:20 +02:00
|
|
|
|
|
|
|
|
int offset = 0;
|
|
|
|
|
int relativePos = ts.positionAtColumn(text,
|
|
|
|
|
d->m_findScopeVerticalBlockSelectionFirstColumn,
|
|
|
|
|
&offset);
|
|
|
|
|
QTextLine line = layout->lineForTextPosition(relativePos);
|
|
|
|
|
qreal x = line.cursorToX(relativePos) + offset * spacew;
|
|
|
|
|
|
|
|
|
|
int eoffset = 0;
|
|
|
|
|
int erelativePos = ts.positionAtColumn(text,
|
|
|
|
|
d->m_findScopeVerticalBlockSelectionLastColumn,
|
|
|
|
|
&eoffset);
|
|
|
|
|
QTextLine eline = layout->lineForTextPosition(erelativePos);
|
|
|
|
|
qreal ex = eline.cursorToX(erelativePos) + eoffset * spacew;
|
|
|
|
|
|
|
|
|
|
QRectF rr = line.naturalTextRect();
|
|
|
|
|
rr.moveTop(rr.top() + r.top());
|
|
|
|
|
rr.setLeft(r.left() + x);
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
if (line.lineNumber() == eline.lineNumber())
|
2010-08-05 15:01:20 +02:00
|
|
|
rr.setRight(r.left() + ex);
|
2014-01-21 14:29:25 +01:00
|
|
|
painter.fillRect(rr, searchScopeFormat.background());
|
2010-08-05 15:01:20 +02:00
|
|
|
|
2014-01-21 14:29:25 +01:00
|
|
|
QColor lineCol = searchScopeFormat.foreground().color();
|
2010-08-05 15:01:20 +02:00
|
|
|
QPen pen = painter.pen();
|
|
|
|
|
painter.setPen(lineCol);
|
|
|
|
|
if (blockFS == d->m_findScopeStart.block())
|
|
|
|
|
painter.drawLine(rr.topLeft(), rr.topRight());
|
|
|
|
|
if (blockFS == d->m_findScopeEnd.block())
|
|
|
|
|
painter.drawLine(rr.bottomLeft(), rr.bottomRight());
|
|
|
|
|
painter.drawLine(rr.topLeft(), rr.bottomLeft());
|
|
|
|
|
painter.drawLine(rr.topRight(), rr.bottomRight());
|
|
|
|
|
painter.setPen(pen);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
offsetFS.ry() += r.height();
|
|
|
|
|
|
|
|
|
|
if (offsetFS.y() > viewportRect.height())
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
blockFS = blockFS.next();
|
|
|
|
|
if (!blockFS.isVisible()) {
|
|
|
|
|
// invisible blocks do have zero line count
|
|
|
|
|
blockFS = doc->findBlockByLineNumber(blockFS.firstLineNumber());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-02 13:47:45 +01:00
|
|
|
if (!d->m_findScopeStart.isNull() && d->m_findScopeVerticalBlockSelectionFirstColumn < 0) {
|
|
|
|
|
|
|
|
|
|
TextEditorOverlay *overlay = new TextEditorOverlay(this);
|
|
|
|
|
overlay->addOverlaySelection(d->m_findScopeStart.position(),
|
|
|
|
|
d->m_findScopeEnd.position(),
|
2014-01-21 14:29:25 +01:00
|
|
|
searchScopeFormat.foreground().color(),
|
|
|
|
|
searchScopeFormat.background().color(),
|
2010-11-02 13:47:45 +01:00
|
|
|
TextEditorOverlay::ExpandBegin);
|
|
|
|
|
overlay->setAlpha(false);
|
|
|
|
|
overlay->paint(&painter, e->rect());
|
|
|
|
|
delete overlay;
|
|
|
|
|
}
|
|
|
|
|
|
2010-08-05 15:01:20 +02:00
|
|
|
|
|
|
|
|
|
2009-12-02 10:57:05 +01:00
|
|
|
d->m_searchResultOverlay->fill(&painter,
|
2014-01-21 14:29:25 +01:00
|
|
|
fs.toTextCharFormat(C_SEARCH_RESULT).background().color(),
|
2009-12-02 10:57:05 +01:00
|
|
|
e->rect());
|
2009-10-08 12:57:26 +02:00
|
|
|
|
2017-10-06 14:51:22 +02:00
|
|
|
{ // remove all annotation rects from the cache that where drawn before the first visible block
|
|
|
|
|
auto it = d->m_annotationRects.begin();
|
|
|
|
|
auto end = d->m_annotationRects.end();
|
|
|
|
|
while (it != end && it.key() < block.blockNumber())
|
|
|
|
|
it = d->m_annotationRects.erase(it);
|
|
|
|
|
}
|
2009-04-23 17:28:53 +02:00
|
|
|
|
2009-12-02 10:57:05 +01:00
|
|
|
while (block.isValid()) {
|
|
|
|
|
|
|
|
|
|
QRectF r = blockBoundingRect(block).translated(offset);
|
|
|
|
|
|
|
|
|
|
if (r.bottom() >= er.top() && r.top() <= er.bottom()) {
|
2009-04-23 17:28:53 +02:00
|
|
|
|
2009-12-02 10:57:05 +01:00
|
|
|
QTextLayout *layout = block.layout();
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-12-02 10:57:05 +01:00
|
|
|
QTextOption option = layout->textOption();
|
2014-09-26 09:14:03 +02:00
|
|
|
if (suppressSyntaxInIfdefedOutBlock && TextDocumentLayout::ifdefedOut(block)) {
|
2010-03-05 12:33:33 +01:00
|
|
|
option.setFlags(option.flags() | QTextOption::SuppressColors);
|
2014-01-21 14:29:25 +01:00
|
|
|
painter.setPen(ifdefedOutFormat.foreground().color());
|
2009-12-02 10:57:05 +01:00
|
|
|
} else {
|
|
|
|
|
option.setFlags(option.flags() & ~QTextOption::SuppressColors);
|
|
|
|
|
painter.setPen(context.palette.text().color());
|
|
|
|
|
}
|
|
|
|
|
layout->setTextOption(option);
|
2010-04-16 13:04:33 +02:00
|
|
|
layout->setFont(doc->defaultFont()); // this really should be in qplaintextedit when creating the layout!
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
int blpos = block.position();
|
|
|
|
|
int bllen = block.length();
|
|
|
|
|
|
|
|
|
|
QVector<QTextLayout::FormatRange> selections;
|
2009-05-04 13:46:50 +02:00
|
|
|
QVector<QTextLayout::FormatRange> prioritySelections;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
for (int i = 0; i < context.selections.size(); ++i) {
|
|
|
|
|
const QAbstractTextDocumentLayout::Selection &range = context.selections.at(i);
|
|
|
|
|
const int selStart = range.cursor.selectionStart() - blpos;
|
|
|
|
|
const int selEnd = range.cursor.selectionEnd() - blpos;
|
2010-05-21 16:06:31 +02:00
|
|
|
if (selStart < bllen && selEnd >= 0
|
2010-05-19 10:36:05 +02:00
|
|
|
&& selEnd >= selStart) {
|
2008-12-02 12:01:29 +01:00
|
|
|
QTextLayout::FormatRange o;
|
|
|
|
|
o.start = selStart;
|
|
|
|
|
o.length = selEnd - selStart;
|
|
|
|
|
o.format = range.format;
|
2010-08-05 15:01:20 +02:00
|
|
|
if (i == blockSelectionIndex) {
|
|
|
|
|
QString text = block.text();
|
2014-01-17 15:54:59 +01:00
|
|
|
const TabSettings &ts = d->m_document->tabSettings();
|
2014-06-16 14:20:36 +02:00
|
|
|
o.start = ts.positionAtColumn(text, d->m_blockSelection.firstVisualColumn());
|
|
|
|
|
o.length = ts.positionAtColumn(text, d->m_blockSelection.lastVisualColumn()) - o.start;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2009-05-04 13:46:50 +02:00
|
|
|
if ((hasMainSelection && i == context.selections.size()-1)
|
|
|
|
|
|| (o.format.foreground().style() == Qt::NoBrush
|
|
|
|
|
&& o.format.underlineStyle() != QTextCharFormat::NoUnderline
|
2013-04-09 10:26:31 +02:00
|
|
|
&& o.format.background() == Qt::NoBrush)) {
|
|
|
|
|
if (selectionVisible(block.blockNumber()))
|
|
|
|
|
prioritySelections.append(o);
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
else
|
|
|
|
|
selections.append(o);
|
2010-04-14 13:39:36 +02:00
|
|
|
}
|
|
|
|
|
#if 0
|
|
|
|
|
// we disable fullwidth selection. It's only used for m_highlightCurrentLine which we
|
|
|
|
|
// do differently now
|
|
|
|
|
else if (!range.cursor.hasSelection() && range.format.hasProperty(QTextFormat::FullWidthSelection)
|
2009-04-23 17:28:53 +02:00
|
|
|
&& block.contains(range.cursor.position())) {
|
2008-12-02 12:01:29 +01:00
|
|
|
// for full width selections we don't require an actual selection, just
|
|
|
|
|
// a position to specify the line. that's more convenience in usage.
|
|
|
|
|
QTextLayout::FormatRange o;
|
|
|
|
|
QTextLine l = layout->lineForTextPosition(range.cursor.position() - blpos);
|
|
|
|
|
o.start = l.textStart();
|
|
|
|
|
o.length = l.textLength();
|
|
|
|
|
if (o.start + o.length == bllen - 1)
|
|
|
|
|
++o.length; // include newline
|
|
|
|
|
o.format = range.format;
|
2009-05-04 13:46:50 +02:00
|
|
|
selections.append(o);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2010-04-14 13:39:36 +02:00
|
|
|
#endif
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2009-05-04 13:46:50 +02:00
|
|
|
selections += prioritySelections;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2010-04-14 13:39:36 +02:00
|
|
|
if (d->m_highlightCurrentLine && block == textCursorBlock) {
|
|
|
|
|
|
|
|
|
|
QRectF rr = layout->lineForTextPosition(textCursor().positionInBlock()).rect();
|
|
|
|
|
rr.moveTop(rr.top() + r.top());
|
|
|
|
|
rr.setLeft(0);
|
|
|
|
|
rr.setRight(viewportRect.width() - offset.x());
|
2014-01-21 14:29:25 +01:00
|
|
|
QColor color = fs.toTextCharFormat(C_CURRENT_LINE).background().color();
|
2010-05-20 17:00:10 +02:00
|
|
|
// set alpha, otherwise we cannot see block highlighting and find scope underneath
|
|
|
|
|
color.setAlpha(128);
|
2015-04-24 10:40:23 +02:00
|
|
|
if (!editable && !er.contains(rr.toRect())) {
|
|
|
|
|
QRect updateRect = er;
|
|
|
|
|
updateRect.setLeft(0);
|
|
|
|
|
updateRect.setRight(viewportRect.width() - offset.x());
|
|
|
|
|
viewport()->update(updateRect);
|
|
|
|
|
}
|
2010-05-20 17:00:10 +02:00
|
|
|
painter.fillRect(rr, color);
|
2010-04-14 13:39:36 +02:00
|
|
|
}
|
|
|
|
|
|
2010-08-05 15:01:20 +02:00
|
|
|
|
|
|
|
|
QRectF blockSelectionCursorRect;
|
|
|
|
|
if (d->m_inBlockSelectionMode
|
2014-06-16 14:20:36 +02:00
|
|
|
&& block.blockNumber() >= d->m_blockSelection.firstBlockNumber()
|
|
|
|
|
&& block.blockNumber() <= d->m_blockSelection.lastBlockNumber()) {
|
2010-08-05 15:01:20 +02:00
|
|
|
QString text = block.text();
|
2014-01-17 15:54:59 +01:00
|
|
|
const TabSettings &ts = d->m_document->tabSettings();
|
2014-06-16 14:20:36 +02:00
|
|
|
const qreal spacew = QFontMetricsF(font()).width(QLatin1Char(' '));
|
|
|
|
|
const int cursorw = overwriteMode() ? QFontMetrics(font()).width(QLatin1Char(' '))
|
|
|
|
|
: cursorWidth();
|
2010-08-05 15:01:20 +02:00
|
|
|
|
|
|
|
|
int offset = 0;
|
2014-06-16 14:20:36 +02:00
|
|
|
int relativePos = ts.positionAtColumn(text, d->m_blockSelection.firstVisualColumn(),
|
|
|
|
|
&offset);
|
|
|
|
|
const QTextLine line = layout->lineForTextPosition(relativePos);
|
|
|
|
|
const qreal x = line.cursorToX(relativePos) + offset * spacew;
|
2010-08-05 15:01:20 +02:00
|
|
|
|
|
|
|
|
int eoffset = 0;
|
2014-06-16 14:20:36 +02:00
|
|
|
int erelativePos = ts.positionAtColumn(text, d->m_blockSelection.lastVisualColumn(),
|
|
|
|
|
&eoffset);
|
|
|
|
|
const QTextLine eline = layout->lineForTextPosition(erelativePos);
|
|
|
|
|
const qreal ex = eline.cursorToX(erelativePos) + eoffset * spacew;
|
2010-08-05 15:01:20 +02:00
|
|
|
|
|
|
|
|
QRectF rr = line.naturalTextRect();
|
|
|
|
|
rr.moveTop(rr.top() + r.top());
|
|
|
|
|
rr.setLeft(r.left() + x);
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
if (line.lineNumber() == eline.lineNumber())
|
2010-08-05 15:01:20 +02:00
|
|
|
rr.setRight(r.left() + ex);
|
|
|
|
|
painter.fillRect(rr, palette().highlight());
|
2014-06-16 14:20:36 +02:00
|
|
|
if (d->m_cursorVisible
|
|
|
|
|
&& d->m_blockSelection.firstVisualColumn()
|
|
|
|
|
== d->m_blockSelection.positionColumn) {
|
|
|
|
|
if (overwriteMode() && offset == 0
|
|
|
|
|
&& relativePos < text.length()
|
|
|
|
|
&& text.at(relativePos) != QLatin1Char('\t')
|
|
|
|
|
&& text.at(relativePos) != QLatin1Char('\n')) {
|
|
|
|
|
selections.append(createBlockCursorCharFormatRange(relativePos, palette()));
|
|
|
|
|
} else {
|
|
|
|
|
blockSelectionCursorRect = rr;
|
|
|
|
|
blockSelectionCursorRect.setRight(rr.left() + cursorw);
|
|
|
|
|
}
|
2010-08-05 15:01:20 +02:00
|
|
|
}
|
|
|
|
|
for (int i = line.lineNumber() + 1; i < eline.lineNumber(); ++i) {
|
|
|
|
|
rr = layout->lineAt(i).naturalTextRect();
|
|
|
|
|
rr.moveTop(rr.top() + r.top());
|
|
|
|
|
painter.fillRect(rr, palette().highlight());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rr = eline.naturalTextRect();
|
|
|
|
|
rr.moveTop(rr.top() + r.top());
|
|
|
|
|
rr.setRight(r.left() + ex);
|
|
|
|
|
if (line.lineNumber() != eline.lineNumber())
|
|
|
|
|
painter.fillRect(rr, palette().highlight());
|
2014-06-16 14:20:36 +02:00
|
|
|
if (d->m_cursorVisible
|
|
|
|
|
&& d->m_blockSelection.lastVisualColumn()
|
|
|
|
|
== d->m_blockSelection.positionColumn) {
|
|
|
|
|
if (overwriteMode() && eoffset == 0
|
|
|
|
|
&& erelativePos < text.length()
|
|
|
|
|
&& text.at(erelativePos) != QLatin1Char('\t')
|
|
|
|
|
&& text.at(erelativePos) != QLatin1Char('\n')) {
|
|
|
|
|
selections.append(createBlockCursorCharFormatRange(erelativePos, palette()));
|
|
|
|
|
} else {
|
|
|
|
|
blockSelectionCursorRect = rr;
|
|
|
|
|
blockSelectionCursorRect.setLeft(rr.right());
|
|
|
|
|
blockSelectionCursorRect.setRight(rr.right() + cursorw);
|
|
|
|
|
}
|
2010-08-05 15:01:20 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2010-04-16 13:04:33 +02:00
|
|
|
bool drawCursor = ((editable || true) // we want the cursor in read-only mode
|
|
|
|
|
&& context.cursorPosition >= blpos
|
|
|
|
|
&& context.cursorPosition < blpos + bllen);
|
|
|
|
|
|
2014-06-16 14:20:36 +02:00
|
|
|
bool drawCursorAsBlock = drawCursor && overwriteMode() && !d->m_inBlockSelectionMode;
|
2010-04-16 13:04:33 +02:00
|
|
|
|
|
|
|
|
if (drawCursorAsBlock) {
|
|
|
|
|
int relativePos = context.cursorPosition - blpos;
|
2010-04-16 13:25:44 +02:00
|
|
|
bool doSelection = true;
|
2010-04-16 13:04:33 +02:00
|
|
|
QTextLine line = layout->lineForTextPosition(relativePos);
|
|
|
|
|
qreal x = line.cursorToX(relativePos);
|
|
|
|
|
qreal w = 0;
|
|
|
|
|
if (relativePos < line.textLength() - line.textStart()) {
|
|
|
|
|
w = line.cursorToX(relativePos + 1) - x;
|
|
|
|
|
if (doc->characterAt(context.cursorPosition) == QLatin1Char('\t')) {
|
2010-04-16 13:25:44 +02:00
|
|
|
doSelection = false;
|
2010-08-06 16:23:13 +02:00
|
|
|
qreal space = QFontMetricsF(layout->font()).width(QLatin1Char(' '));
|
2010-04-16 13:04:33 +02:00
|
|
|
if (w > space) {
|
|
|
|
|
x += w-space;
|
|
|
|
|
w = space;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else
|
|
|
|
|
w = QFontMetrics(layout->font()).width(QLatin1Char(' ')); // in sync with QTextLine::draw()
|
|
|
|
|
|
|
|
|
|
QRectF rr = line.rect();
|
|
|
|
|
rr.moveTop(rr.top() + r.top());
|
|
|
|
|
rr.moveLeft(r.left() + x);
|
|
|
|
|
rr.setWidth(w);
|
|
|
|
|
painter.fillRect(rr, palette().text());
|
2014-06-16 14:20:36 +02:00
|
|
|
if (doSelection)
|
|
|
|
|
selections.append(createBlockCursorCharFormatRange(relativePos, palette()));
|
2010-04-16 13:04:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-09-25 15:48:21 +02:00
|
|
|
paintBlock(&painter, block, offset, selections, er);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
if ((drawCursor && !drawCursorAsBlock)
|
2014-06-16 14:20:36 +02:00
|
|
|
|| (editable && context.cursorPosition < -1 && !layout->preeditAreaText().isEmpty())) {
|
2008-12-02 12:01:29 +01:00
|
|
|
int cpos = context.cursorPosition;
|
|
|
|
|
if (cpos < -1)
|
|
|
|
|
cpos = layout->preeditAreaPosition() - (cpos + 2);
|
|
|
|
|
else
|
|
|
|
|
cpos -= blpos;
|
2009-11-25 15:55:45 +01:00
|
|
|
cursor_layout = layout;
|
|
|
|
|
cursor_offset = offset;
|
|
|
|
|
cursor_cpos = cpos;
|
|
|
|
|
cursor_pen = painter.pen();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2010-08-05 15:01:20 +02:00
|
|
|
|
2014-06-16 14:20:36 +02:00
|
|
|
if ((!HostOsInfo::isMacHost()
|
|
|
|
|
|| d->m_blockSelection.positionColumn == d->m_blockSelection.anchorColumn)
|
|
|
|
|
&& blockSelectionCursorRect.isValid())
|
2010-08-05 15:01:20 +02:00
|
|
|
painter.fillRect(blockSelectionCursorRect, palette().text());
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2017-10-17 08:23:41 +02:00
|
|
|
d->updateLineAnnotation(painter, block, lineX < viewportRect.width() ? lineX : 0, er);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
offset.ry() += r.height();
|
|
|
|
|
|
|
|
|
|
if (offset.y() > viewportRect.height())
|
|
|
|
|
break;
|
2009-12-02 10:57:05 +01:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
block = block.next();
|
2009-12-02 10:57:05 +01:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
if (!block.isVisible()) {
|
2010-05-20 15:10:26 +02:00
|
|
|
if (block.blockNumber() == d->visibleFoldedBlockNumber) {
|
2008-12-02 12:01:29 +01:00
|
|
|
visibleCollapsedBlock = block;
|
2014-08-07 18:46:38 +02:00
|
|
|
visibleCollapsedBlockOffset = offset;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// invisible blocks do have zero line count
|
|
|
|
|
block = doc->findBlockByLineNumber(block.firstLineNumber());
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-10-06 14:51:22 +02:00
|
|
|
|
|
|
|
|
// remove all annotation rects from the cache that where drawn after the last visible block
|
|
|
|
|
if (block.isValid()) {
|
|
|
|
|
auto it = d->m_annotationRects.begin();
|
|
|
|
|
auto end = d->m_annotationRects.end();
|
|
|
|
|
while (it != end) {
|
|
|
|
|
if (it.key() > block.blockNumber())
|
|
|
|
|
it = d->m_annotationRects.erase(it);
|
|
|
|
|
else
|
|
|
|
|
++it;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-10-05 18:49:03 +02:00
|
|
|
painter.setPen(context.palette.text().color());
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
if (backgroundVisible() && !block.isValid() && offset.y() <= er.bottom()
|
|
|
|
|
&& (centerOnScroll() || verticalScrollBar()->maximum() == verticalScrollBar()->minimum())) {
|
|
|
|
|
painter.fillRect(QRect(QPoint((int)er.left(), (int)offset.y()), er.bottomRight()), palette().background());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//end QPlainTextEdit::paintEvent()
|
|
|
|
|
|
|
|
|
|
offset = contentOffset();
|
|
|
|
|
block = firstVisibleBlock();
|
|
|
|
|
|
2010-05-31 15:03:38 +02:00
|
|
|
qreal top = blockBoundingGeometry(block).translated(offset).top();
|
|
|
|
|
qreal bottom = top + blockBoundingRect(block).height();
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
|
bool hasSelection = cursor.hasSelection();
|
|
|
|
|
int selectionStart = cursor.selectionStart();
|
|
|
|
|
int selectionEnd = cursor.selectionEnd();
|
|
|
|
|
|
|
|
|
|
while (block.isValid() && top <= e->rect().bottom()) {
|
|
|
|
|
QTextBlock nextBlock = block.next();
|
|
|
|
|
QTextBlock nextVisibleBlock = nextBlock;
|
|
|
|
|
|
2009-10-05 19:14:31 +02:00
|
|
|
if (!nextVisibleBlock.isVisible()) {
|
2008-12-02 12:01:29 +01:00
|
|
|
// invisible blocks do have zero line count
|
|
|
|
|
nextVisibleBlock = doc->findBlockByLineNumber(nextVisibleBlock.firstLineNumber());
|
2009-10-05 19:14:31 +02:00
|
|
|
// paranoia in case our code somewhere did not set the line count
|
|
|
|
|
// of the invisible block to 0
|
|
|
|
|
while (nextVisibleBlock.isValid() && !nextVisibleBlock.isVisible())
|
|
|
|
|
nextVisibleBlock = nextVisibleBlock.next();
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
if (block.isVisible() && bottom >= e->rect().top()) {
|
|
|
|
|
if (d->m_displaySettings.m_visualizeWhitespace) {
|
|
|
|
|
QTextLayout *layout = block.layout();
|
|
|
|
|
int lineCount = layout->lineCount();
|
|
|
|
|
if (lineCount >= 2 || !nextBlock.isValid()) {
|
|
|
|
|
painter.save();
|
2017-10-10 08:56:05 +02:00
|
|
|
painter.setPen(fs.toTextCharFormat(C_VISUAL_WHITESPACE).foreground().color());
|
2008-12-02 12:01:29 +01:00
|
|
|
for (int i = 0; i < lineCount-1; ++i) { // paint line wrap indicator
|
|
|
|
|
QTextLine line = layout->lineAt(i);
|
|
|
|
|
QRectF lineRect = line.naturalTextRect().translated(offset.x(), top);
|
|
|
|
|
QChar visualArrow((ushort)0x21b5);
|
2010-05-31 15:03:38 +02:00
|
|
|
painter.drawText(QPointF(lineRect.right(),
|
|
|
|
|
lineRect.top() + line.ascent()),
|
|
|
|
|
visualArrow);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
if (!nextBlock.isValid()) { // paint EOF symbol
|
|
|
|
|
QTextLine line = layout->lineAt(lineCount-1);
|
|
|
|
|
QRectF lineRect = line.naturalTextRect().translated(offset.x(), top);
|
|
|
|
|
int h = 4;
|
|
|
|
|
lineRect.adjust(0, 0, -1, -1);
|
|
|
|
|
QPainterPath path;
|
|
|
|
|
QPointF pos(lineRect.topRight() + QPointF(h+4, line.ascent()));
|
|
|
|
|
path.moveTo(pos);
|
|
|
|
|
path.lineTo(pos + QPointF(-h, -h));
|
|
|
|
|
path.lineTo(pos + QPointF(0, -2*h));
|
|
|
|
|
path.lineTo(pos + QPointF(h, -h));
|
|
|
|
|
path.closeSubpath();
|
|
|
|
|
painter.setBrush(painter.pen().color());
|
|
|
|
|
painter.drawPath(path);
|
|
|
|
|
}
|
|
|
|
|
painter.restore();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-04-10 13:51:04 +02:00
|
|
|
if (nextBlock.isValid() && !nextBlock.isVisible() && replacementVisible(block.blockNumber())) {
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
bool selectThis = (hasSelection
|
|
|
|
|
&& nextBlock.position() >= selectionStart
|
|
|
|
|
&& nextBlock.position() < selectionEnd);
|
2013-06-05 15:25:42 +02:00
|
|
|
painter.save();
|
2008-12-02 12:01:29 +01:00
|
|
|
if (selectThis) {
|
|
|
|
|
painter.setBrush(palette().highlight());
|
2013-06-05 15:25:42 +02:00
|
|
|
} else {
|
|
|
|
|
QColor rc = replacementPenColor(block.blockNumber());
|
|
|
|
|
if (rc.isValid())
|
|
|
|
|
painter.setPen(rc);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QTextLayout *layout = block.layout();
|
|
|
|
|
QTextLine line = layout->lineAt(layout->lineCount()-1);
|
|
|
|
|
QRectF lineRect = line.naturalTextRect().translated(offset.x(), top);
|
|
|
|
|
lineRect.adjust(0, 0, -1, -1);
|
|
|
|
|
|
2011-09-14 22:16:28 +02:00
|
|
|
QString replacement = foldReplacementText(block);
|
|
|
|
|
QString rectReplacement = QLatin1String(" {") + replacement + QLatin1String("}; ");
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
QRectF collapseRect(lineRect.right() + 12,
|
|
|
|
|
lineRect.top(),
|
2011-09-14 22:16:28 +02:00
|
|
|
fontMetrics().width(rectReplacement),
|
2008-12-02 12:01:29 +01:00
|
|
|
lineRect.height());
|
|
|
|
|
painter.setRenderHint(QPainter::Antialiasing, true);
|
|
|
|
|
painter.translate(.5, .5);
|
|
|
|
|
painter.drawRoundedRect(collapseRect.adjusted(0, 0, 0, -1), 3, 3);
|
|
|
|
|
painter.setRenderHint(QPainter::Antialiasing, false);
|
|
|
|
|
painter.translate(-.5, -.5);
|
|
|
|
|
|
2014-09-26 09:14:03 +02:00
|
|
|
if (TextBlockUserData *nextBlockUserData = TextDocumentLayout::testUserData(nextBlock)) {
|
2010-05-20 15:10:26 +02:00
|
|
|
if (nextBlockUserData->foldingStartIncluded())
|
|
|
|
|
replacement.prepend(nextBlock.text().trimmed().left(1));
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
block = nextVisibleBlock.previous();
|
|
|
|
|
if (!block.isValid())
|
|
|
|
|
block = doc->lastBlock();
|
|
|
|
|
|
2014-09-26 09:14:03 +02:00
|
|
|
if (TextBlockUserData *blockUserData = TextDocumentLayout::testUserData(block)) {
|
2010-05-20 15:10:26 +02:00
|
|
|
if (blockUserData->foldingEndIncluded()) {
|
|
|
|
|
QString right = block.text().trimmed();
|
|
|
|
|
if (right.endsWith(QLatin1Char(';'))) {
|
|
|
|
|
right.chop(1);
|
|
|
|
|
right = right.trimmed();
|
2017-04-14 10:24:28 +02:00
|
|
|
replacement.append(right.rightRef(right.endsWith('/') ? 2 : 1));
|
2010-05-20 15:10:26 +02:00
|
|
|
replacement.append(QLatin1Char(';'));
|
|
|
|
|
} else {
|
2017-04-14 10:24:28 +02:00
|
|
|
replacement.append(right.rightRef(right.endsWith('/') ? 2 : 1));
|
2010-05-20 15:10:26 +02:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
2010-05-20 15:10:26 +02:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
if (selectThis)
|
|
|
|
|
painter.setPen(palette().highlightedText().color());
|
|
|
|
|
painter.drawText(collapseRect, Qt::AlignCenter, replacement);
|
2013-06-05 15:25:42 +02:00
|
|
|
painter.restore();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
block = nextVisibleBlock;
|
|
|
|
|
top = bottom;
|
2010-05-31 15:03:38 +02:00
|
|
|
bottom = top + blockBoundingRect(block).height();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2016-04-28 12:51:34 +02:00
|
|
|
d->updateAnimator(d->m_bracketsAnimator, painter);
|
|
|
|
|
d->updateAnimator(d->m_autocompleteAnimator, painter);
|
2009-04-28 18:34:58 +02:00
|
|
|
|
2010-08-05 15:01:20 +02:00
|
|
|
// draw the overlays, but only if we do not have a find scope, otherwise the
|
|
|
|
|
// view becomes too noisy.
|
|
|
|
|
if (d->m_findScopeStart.isNull()) {
|
|
|
|
|
if (d->m_overlay->isVisible())
|
|
|
|
|
d->m_overlay->paint(&painter, e->rect());
|
2010-01-19 12:28:50 +01:00
|
|
|
|
2010-08-05 15:01:20 +02:00
|
|
|
if (d->m_snippetOverlay->isVisible())
|
|
|
|
|
d->m_snippetOverlay->paint(&painter, e->rect());
|
|
|
|
|
|
|
|
|
|
if (!d->m_refactorOverlay->isEmpty())
|
|
|
|
|
d->m_refactorOverlay->paint(&painter, e->rect());
|
|
|
|
|
}
|
2010-01-29 22:49:55 +01:00
|
|
|
|
2009-12-02 10:57:05 +01:00
|
|
|
if (!d->m_searchResultOverlay->isEmpty()) {
|
2009-12-01 14:47:10 +01:00
|
|
|
d->m_searchResultOverlay->paint(&painter, e->rect());
|
2009-12-02 10:57:05 +01:00
|
|
|
d->m_searchResultOverlay->clear();
|
|
|
|
|
}
|
2009-11-25 15:55:45 +01:00
|
|
|
|
2010-07-02 13:47:14 +02:00
|
|
|
|
2009-11-25 15:55:45 +01:00
|
|
|
// draw the cursor last, on top of everything
|
2010-08-05 15:01:20 +02:00
|
|
|
if (cursor_layout && !d->m_inBlockSelectionMode) {
|
2009-11-25 15:55:45 +01:00
|
|
|
painter.setPen(cursor_pen);
|
|
|
|
|
cursor_layout->drawCursor(&painter, cursor_offset, cursor_cpos, cursorWidth());
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-31 13:48:40 +02:00
|
|
|
if (visibleCollapsedBlock.isValid()) {
|
|
|
|
|
drawCollapsedBlockPopup(painter,
|
|
|
|
|
visibleCollapsedBlock,
|
|
|
|
|
visibleCollapsedBlockOffset,
|
|
|
|
|
er);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::paintBlock(QPainter *painter,
|
|
|
|
|
const QTextBlock &block,
|
|
|
|
|
const QPointF &offset,
|
|
|
|
|
const QVector<QTextLayout::FormatRange> &selections,
|
|
|
|
|
const QRect &clipRect) const
|
2014-09-25 15:48:21 +02:00
|
|
|
{
|
|
|
|
|
block.layout()->draw(painter, offset, selections, clipRect);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
int TextEditorWidget::visibleFoldedBlockNumber() const
|
2013-04-25 17:37:20 +02:00
|
|
|
{
|
|
|
|
|
return d->visibleFoldedBlockNumber;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::drawCollapsedBlockPopup(QPainter &painter,
|
2010-05-31 13:48:40 +02:00
|
|
|
const QTextBlock &block,
|
|
|
|
|
QPointF offset,
|
|
|
|
|
const QRect &clip)
|
|
|
|
|
{
|
|
|
|
|
int margin = block.document()->documentMargin();
|
|
|
|
|
qreal maxWidth = 0;
|
|
|
|
|
qreal blockHeight = 0;
|
|
|
|
|
QTextBlock b = block;
|
|
|
|
|
|
|
|
|
|
while (!b.isVisible()) {
|
|
|
|
|
b.setVisible(true); // make sure block bounding rect works
|
|
|
|
|
QRectF r = blockBoundingRect(b).translated(offset);
|
|
|
|
|
|
|
|
|
|
QTextLayout *layout = b.layout();
|
|
|
|
|
for (int i = layout->lineCount()-1; i >= 0; --i)
|
|
|
|
|
maxWidth = qMax(maxWidth, layout->lineAt(i).naturalTextWidth() + 2*margin);
|
|
|
|
|
|
|
|
|
|
blockHeight += r.height();
|
|
|
|
|
|
|
|
|
|
b.setVisible(false); // restore previous state
|
|
|
|
|
b.setLineCount(0); // restore 0 line count for invisible block
|
|
|
|
|
b = b.next();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
painter.save();
|
|
|
|
|
painter.setRenderHint(QPainter::Antialiasing, true);
|
|
|
|
|
painter.translate(.5, .5);
|
|
|
|
|
QBrush brush = palette().base();
|
2014-01-21 14:29:25 +01:00
|
|
|
const QTextCharFormat &ifdefedOutFormat
|
2014-08-01 23:31:56 +02:00
|
|
|
= textDocument()->fontSettings().toTextCharFormat(C_DISABLED_CODE);
|
2014-01-21 14:29:25 +01:00
|
|
|
if (ifdefedOutFormat.hasProperty(QTextFormat::BackgroundBrush))
|
|
|
|
|
brush = ifdefedOutFormat.background();
|
2010-05-31 13:48:40 +02:00
|
|
|
painter.setBrush(brush);
|
|
|
|
|
painter.drawRoundedRect(QRectF(offset.x(),
|
|
|
|
|
offset.y(),
|
|
|
|
|
maxWidth, blockHeight).adjusted(0, 0, 0, 0), 3, 3);
|
|
|
|
|
painter.restore();
|
|
|
|
|
|
|
|
|
|
QTextBlock end = b;
|
|
|
|
|
b = block;
|
|
|
|
|
while (b != end) {
|
|
|
|
|
b.setVisible(true); // make sure block bounding rect works
|
|
|
|
|
QRectF r = blockBoundingRect(b).translated(offset);
|
|
|
|
|
QTextLayout *layout = b.layout();
|
|
|
|
|
QVector<QTextLayout::FormatRange> selections;
|
|
|
|
|
layout->draw(&painter, offset, selections, clip);
|
|
|
|
|
|
|
|
|
|
b.setVisible(false); // restore previous state
|
2010-08-05 15:01:20 +02:00
|
|
|
b.setLineCount(0); // restore 0 line count for invisible block
|
2010-05-31 13:48:40 +02:00
|
|
|
offset.ry() += r.height();
|
|
|
|
|
b = b.next();
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
QWidget *TextEditorWidget::extraArea() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
return d->m_extraArea;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
int TextEditorWidget::extraAreaWidth(int *markWidthPtr) const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-09-26 09:14:03 +02:00
|
|
|
TextDocumentLayout *documentLayout = qobject_cast<TextDocumentLayout*>(document()->documentLayout());
|
2008-12-02 12:01:29 +01:00
|
|
|
if (!documentLayout)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (!d->m_marksVisible && documentLayout->hasMarks)
|
|
|
|
|
d->m_marksVisible = true;
|
|
|
|
|
|
2017-09-05 12:01:13 +02:00
|
|
|
if (!d->m_marksVisible && !d->m_lineNumbersVisible && !d->m_codeFoldingVisible)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
int space = 0;
|
|
|
|
|
const QFontMetrics fm(d->m_extraArea->fontMetrics());
|
|
|
|
|
|
|
|
|
|
if (d->m_lineNumbersVisible) {
|
2009-08-20 16:13:24 +02:00
|
|
|
QFont fnt = d->m_extraArea->font();
|
2011-03-21 17:32:53 +01:00
|
|
|
// this works under the assumption that bold or italic
|
|
|
|
|
// can only make a font wider
|
2014-01-21 14:29:25 +01:00
|
|
|
const QTextCharFormat ¤tLineNumberFormat
|
2014-08-01 23:31:56 +02:00
|
|
|
= textDocument()->fontSettings().toTextCharFormat(C_CURRENT_LINE_NUMBER);
|
2014-01-21 14:29:25 +01:00
|
|
|
fnt.setBold(currentLineNumberFormat.font().bold());
|
|
|
|
|
fnt.setItalic(currentLineNumberFormat.font().italic());
|
2009-08-20 16:13:24 +02:00
|
|
|
const QFontMetrics linefm(fnt);
|
|
|
|
|
|
2013-01-20 23:09:09 +01:00
|
|
|
space += linefm.width(QLatin1Char('9')) * lineNumberDigits();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
int markWidth = 0;
|
|
|
|
|
|
|
|
|
|
if (d->m_marksVisible) {
|
2012-02-29 13:27:02 +01:00
|
|
|
markWidth += documentLayout->maxMarkWidthFactor * fm.lineSpacing() + 2;
|
2011-03-23 10:45:04 +01:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
// if (documentLayout->doubleMarkCount)
|
|
|
|
|
// markWidth += fm.lineSpacing() / 3;
|
|
|
|
|
space += markWidth;
|
|
|
|
|
} else {
|
|
|
|
|
space += 2;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (markWidthPtr)
|
|
|
|
|
*markWidthPtr = markWidth;
|
|
|
|
|
|
|
|
|
|
space += 4;
|
|
|
|
|
|
|
|
|
|
if (d->m_codeFoldingVisible)
|
2010-05-20 15:10:26 +02:00
|
|
|
space += foldBoxWidth(fm);
|
2008-12-02 12:01:29 +01:00
|
|
|
return space;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::slotUpdateExtraAreaWidth()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-08-01 15:06:28 +02:00
|
|
|
if (q->isLeftToRight())
|
|
|
|
|
q->setViewportMargins(q->extraAreaWidth(), 0, 0, 0);
|
2009-03-17 16:16:36 +01:00
|
|
|
else
|
2014-08-01 15:06:28 +02:00
|
|
|
q->setViewportMargins(0, 0, q->extraAreaWidth(), 0);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2016-06-09 16:52:00 +02:00
|
|
|
static void drawRectBox(QPainter *painter, const QRect &rect, const QPalette &pal)
|
2009-05-04 11:10:22 +02:00
|
|
|
{
|
2009-05-29 15:31:05 +02:00
|
|
|
painter->save();
|
2016-06-09 16:52:00 +02:00
|
|
|
painter->setOpacity(0.5);
|
|
|
|
|
painter->fillRect(rect, pal.brush(QPalette::Highlight));
|
2009-05-29 15:31:05 +02:00
|
|
|
painter->restore();
|
2009-05-04 11:10:22 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::extraAreaPaintEvent(QPaintEvent *e)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
QTextDocument *doc = document();
|
2014-09-26 09:14:03 +02:00
|
|
|
TextDocumentLayout *documentLayout = qobject_cast<TextDocumentLayout*>(doc->documentLayout());
|
2008-12-09 15:25:01 +01:00
|
|
|
QTC_ASSERT(documentLayout, return);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-04-28 12:55:31 +02:00
|
|
|
int selStart = textCursor().selectionStart();
|
|
|
|
|
int selEnd = textCursor().selectionEnd();
|
2009-04-24 16:44:48 +02:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
QPalette pal = d->m_extraArea->palette();
|
|
|
|
|
pal.setCurrentColorGroup(QPalette::Active);
|
|
|
|
|
QPainter painter(d->m_extraArea);
|
2009-08-20 16:13:24 +02:00
|
|
|
const QFontMetrics fm(d->m_extraArea->font());
|
2008-12-02 12:01:29 +01:00
|
|
|
int fmLineSpacing = fm.lineSpacing();
|
|
|
|
|
|
|
|
|
|
int markWidth = 0;
|
|
|
|
|
if (d->m_marksVisible)
|
|
|
|
|
markWidth += fm.lineSpacing();
|
|
|
|
|
|
2010-05-20 15:10:26 +02:00
|
|
|
const int collapseColumnWidth = d->m_codeFoldingVisible ? foldBoxWidth(fm): 0;
|
2009-06-09 18:17:38 +02:00
|
|
|
const int extraAreaWidth = d->m_extraArea->width() - collapseColumnWidth;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2011-06-07 11:05:31 +02:00
|
|
|
painter.fillRect(e->rect(), pal.color(QPalette::Background));
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
QTextBlock block = firstVisibleBlock();
|
|
|
|
|
int blockNumber = block.blockNumber();
|
2010-04-12 15:37:08 +02:00
|
|
|
qreal top = blockBoundingGeometry(block).translated(contentOffset()).top();
|
|
|
|
|
qreal bottom = top;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
while (block.isValid() && top <= e->rect().bottom()) {
|
|
|
|
|
|
|
|
|
|
top = bottom;
|
2010-05-31 15:03:38 +02:00
|
|
|
const qreal height = blockBoundingRect(block).height();
|
|
|
|
|
bottom = top + height;
|
2008-12-02 12:01:29 +01:00
|
|
|
QTextBlock nextBlock = block.next();
|
|
|
|
|
|
|
|
|
|
QTextBlock nextVisibleBlock = nextBlock;
|
|
|
|
|
int nextVisibleBlockNumber = blockNumber + 1;
|
|
|
|
|
|
|
|
|
|
if (!nextVisibleBlock.isVisible()) {
|
|
|
|
|
// invisible blocks do have zero line count
|
|
|
|
|
nextVisibleBlock = doc->findBlockByLineNumber(nextVisibleBlock.firstLineNumber());
|
|
|
|
|
nextVisibleBlockNumber = nextVisibleBlock.blockNumber();
|
|
|
|
|
}
|
|
|
|
|
|
2009-11-09 15:59:48 +01:00
|
|
|
if (bottom < e->rect().top()) {
|
|
|
|
|
block = nextVisibleBlock;
|
|
|
|
|
blockNumber = nextVisibleBlockNumber;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
painter.setPen(pal.color(QPalette::Dark));
|
|
|
|
|
|
2015-04-08 12:56:44 +02:00
|
|
|
if (d->m_lineNumbersVisible) {
|
|
|
|
|
const QString &number = lineNumber(blockNumber);
|
|
|
|
|
bool selected = (
|
|
|
|
|
(selStart < block.position() + block.length()
|
|
|
|
|
|
|
|
|
|
&& selEnd > block.position())
|
|
|
|
|
|| (selStart == selEnd && selStart == block.position())
|
|
|
|
|
);
|
|
|
|
|
if (selected) {
|
|
|
|
|
painter.save();
|
|
|
|
|
QFont f = painter.font();
|
|
|
|
|
const QTextCharFormat ¤tLineNumberFormat
|
|
|
|
|
= textDocument()->fontSettings().toTextCharFormat(C_CURRENT_LINE_NUMBER);
|
|
|
|
|
f.setBold(currentLineNumberFormat.font().bold());
|
|
|
|
|
f.setItalic(currentLineNumberFormat.font().italic());
|
|
|
|
|
painter.setFont(f);
|
|
|
|
|
painter.setPen(currentLineNumberFormat.foreground().color());
|
|
|
|
|
if (currentLineNumberFormat.background() != Qt::NoBrush)
|
|
|
|
|
painter.fillRect(QRect(0, top, extraAreaWidth, height), currentLineNumberFormat.background().color());
|
|
|
|
|
}
|
|
|
|
|
painter.drawText(QRectF(markWidth, top, extraAreaWidth - markWidth - 4, height), Qt::AlignRight, number);
|
|
|
|
|
if (selected)
|
|
|
|
|
painter.restore();
|
|
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
if (d->m_codeFoldingVisible || d->m_marksVisible) {
|
|
|
|
|
painter.save();
|
|
|
|
|
painter.setRenderHint(QPainter::Antialiasing, false);
|
|
|
|
|
|
|
|
|
|
if (TextBlockUserData *userData = static_cast<TextBlockUserData*>(block.userData())) {
|
|
|
|
|
if (d->m_marksVisible) {
|
|
|
|
|
int xoffset = 0;
|
2012-02-29 13:27:02 +01:00
|
|
|
TextMarks marks = userData->marks();
|
|
|
|
|
TextMarks::const_iterator it = marks.constBegin();
|
|
|
|
|
if (marks.size() > 3) {
|
|
|
|
|
// We want the 3 with the highest priority so iterate from the back
|
|
|
|
|
int count = 0;
|
|
|
|
|
it = marks.constEnd() - 1;
|
|
|
|
|
while (it != marks.constBegin()) {
|
2012-10-09 14:02:52 +02:00
|
|
|
if ((*it)->isVisible())
|
2012-02-29 13:27:02 +01:00
|
|
|
++count;
|
|
|
|
|
if (count == 3)
|
|
|
|
|
break;
|
|
|
|
|
--it;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
TextMarks::const_iterator end = marks.constEnd();
|
|
|
|
|
for ( ; it != end; ++it) {
|
2014-07-19 11:27:28 +02:00
|
|
|
TextMark *mark = *it;
|
2012-10-09 14:02:52 +02:00
|
|
|
if (!mark->isVisible())
|
2012-02-13 19:26:16 +01:00
|
|
|
continue;
|
2011-03-21 17:32:53 +01:00
|
|
|
const int height = fmLineSpacing - 1;
|
|
|
|
|
const int width = int(.5 + height * mark->widthFactor());
|
|
|
|
|
const QRect r(xoffset, top, width, height);
|
2017-06-20 08:28:10 +02:00
|
|
|
mark->paintIcon(&painter, r);
|
2008-12-02 12:01:29 +01:00
|
|
|
xoffset += 2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-05-04 12:04:40 +02:00
|
|
|
if (d->m_codeFoldingVisible) {
|
2009-04-29 15:37:38 +02:00
|
|
|
|
2010-05-20 15:10:26 +02:00
|
|
|
int extraAreaHighlightFoldBlockNumber = -1;
|
|
|
|
|
int extraAreaHighlightFoldEndBlockNumber = -1;
|
2009-04-29 15:37:38 +02:00
|
|
|
if (!d->m_highlightBlocksInfo.isEmpty()) {
|
2010-05-20 15:10:26 +02:00
|
|
|
extraAreaHighlightFoldBlockNumber = d->m_highlightBlocksInfo.open.last();
|
|
|
|
|
extraAreaHighlightFoldEndBlockNumber = d->m_highlightBlocksInfo.close.first();
|
2009-04-29 15:37:38 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 09:14:03 +02:00
|
|
|
TextBlockUserData *nextBlockUserData = TextDocumentLayout::testUserData(nextBlock);
|
2009-04-29 15:37:38 +02:00
|
|
|
|
2010-05-20 15:10:26 +02:00
|
|
|
bool drawBox = nextBlockUserData
|
2014-09-26 09:14:03 +02:00
|
|
|
&& TextDocumentLayout::foldingIndent(block) < nextBlockUserData->foldingIndent();
|
2010-05-20 15:10:26 +02:00
|
|
|
|
|
|
|
|
|
2009-04-29 15:37:38 +02:00
|
|
|
|
2010-05-20 15:10:26 +02:00
|
|
|
bool active = blockNumber == extraAreaHighlightFoldBlockNumber;
|
|
|
|
|
bool hovered = blockNumber >= extraAreaHighlightFoldBlockNumber
|
|
|
|
|
&& blockNumber <= extraAreaHighlightFoldEndBlockNumber;
|
2009-04-29 15:37:38 +02:00
|
|
|
|
2010-05-20 15:10:26 +02:00
|
|
|
int boxWidth = foldBoxWidth(fm);
|
2009-05-05 10:20:43 +02:00
|
|
|
if (hovered) {
|
2010-05-06 15:26:55 +02:00
|
|
|
int itop = qRound(top);
|
|
|
|
|
int ibottom = qRound(bottom);
|
|
|
|
|
QRect box = QRect(extraAreaWidth + 1, itop, boxWidth - 2, ibottom - itop);
|
2016-06-09 16:52:00 +02:00
|
|
|
drawRectBox(&painter, box, pal);
|
2009-04-29 15:37:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (drawBox) {
|
2009-05-04 11:10:22 +02:00
|
|
|
bool expanded = nextBlock.isVisible();
|
2009-06-05 16:16:03 +02:00
|
|
|
int size = boxWidth/4;
|
|
|
|
|
QRect box(extraAreaWidth + size, top + size,
|
|
|
|
|
2 * (size) + 1, 2 * (size) + 1);
|
2014-07-25 16:23:50 +02:00
|
|
|
d->drawFoldingMarker(&painter, pal, box, expanded, active, hovered);
|
2009-04-29 15:37:38 +02:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
painter.restore();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (d->m_revisionsVisible && block.revision() != documentLayout->lastSaveRevision) {
|
|
|
|
|
painter.save();
|
|
|
|
|
painter.setRenderHint(QPainter::Antialiasing, false);
|
|
|
|
|
if (block.revision() < 0)
|
|
|
|
|
painter.setPen(QPen(Qt::darkGreen, 2));
|
|
|
|
|
else
|
|
|
|
|
painter.setPen(QPen(Qt::red, 2));
|
2009-07-16 11:27:02 +02:00
|
|
|
painter.drawLine(extraAreaWidth - 1, top, extraAreaWidth - 1, bottom - 1);
|
2008-12-02 12:01:29 +01:00
|
|
|
painter.restore();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
block = nextVisibleBlock;
|
|
|
|
|
blockNumber = nextVisibleBlockNumber;
|
|
|
|
|
}
|
|
|
|
|
}
|
2009-04-24 16:44:48 +02:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::drawFoldingMarker(QPainter *painter, const QPalette &pal,
|
2009-05-05 10:20:43 +02:00
|
|
|
const QRect &rect,
|
|
|
|
|
bool expanded,
|
|
|
|
|
bool active,
|
|
|
|
|
bool hovered) const
|
2009-05-04 11:10:22 +02:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
QStyle *s = q->style();
|
2009-06-08 12:07:39 +02:00
|
|
|
if (ManhattanStyle *ms = qobject_cast<ManhattanStyle*>(s))
|
2010-02-23 12:42:04 +01:00
|
|
|
s = ms->baseStyle();
|
2009-06-08 12:07:39 +02:00
|
|
|
|
2016-06-09 16:59:54 +02:00
|
|
|
QStyleOptionViewItem opt;
|
|
|
|
|
opt.rect = rect;
|
|
|
|
|
opt.state = QStyle::State_Active | QStyle::State_Item | QStyle::State_Children;
|
|
|
|
|
if (expanded)
|
|
|
|
|
opt.state |= QStyle::State_Open;
|
|
|
|
|
if (active)
|
|
|
|
|
opt.state |= QStyle::State_MouseOver | QStyle::State_Enabled | QStyle::State_Selected;
|
|
|
|
|
if (hovered)
|
|
|
|
|
opt.palette.setBrush(QPalette::Window, pal.highlight());
|
|
|
|
|
|
|
|
|
|
const char* const className = s->metaObject()->className();
|
|
|
|
|
|
|
|
|
|
if (!qstrcmp(className, "OxygenStyle")) {
|
|
|
|
|
const QStyle::PrimitiveElement direction = expanded ? QStyle::PE_IndicatorArrowDown
|
|
|
|
|
: QStyle::PE_IndicatorArrowRight;
|
|
|
|
|
StyleHelper::drawArrow(direction, painter, &opt);
|
2009-06-05 16:16:03 +02:00
|
|
|
} else {
|
2009-06-08 12:07:39 +02:00
|
|
|
// QGtkStyle needs a small correction to draw the marker in the right place
|
2016-06-09 16:59:54 +02:00
|
|
|
if (!qstrcmp(className, "QGtkStyle"))
|
2009-06-08 12:07:39 +02:00
|
|
|
opt.rect.translate(-2, 0);
|
2016-06-09 16:59:54 +02:00
|
|
|
else if (!qstrcmp(className, "QMacStyle"))
|
|
|
|
|
opt.rect.translate(-2, 0);
|
|
|
|
|
else if (!qstrcmp(className, "QFusionStyle"))
|
|
|
|
|
opt.rect.translate(0, -1);
|
2009-06-08 12:07:39 +02:00
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
s->drawPrimitive(QStyle::PE_IndicatorBranch, &opt, painter, q);
|
2009-06-05 16:16:03 +02:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::slotUpdateRequest(const QRect &r, int dy)
|
2009-03-17 16:16:36 +01:00
|
|
|
{
|
2013-07-17 00:01:45 +03:00
|
|
|
if (dy) {
|
2014-08-01 15:06:28 +02:00
|
|
|
m_extraArea->scroll(0, dy);
|
2013-07-17 00:01:45 +03:00
|
|
|
} else if (r.width() > 4) { // wider than cursor width, not just cursor blinking
|
2014-08-01 15:06:28 +02:00
|
|
|
m_extraArea->update(0, r.y(), m_extraArea->width(), r.height());
|
|
|
|
|
if (!m_searchExpr.isEmpty()) {
|
|
|
|
|
const int m = m_searchResultOverlay->dropShadowWidth();
|
|
|
|
|
q->viewport()->update(r.adjusted(-m, -m, m, m));
|
2009-12-02 11:29:38 +01:00
|
|
|
}
|
2009-03-17 16:16:36 +01:00
|
|
|
}
|
|
|
|
|
|
2014-08-01 15:06:28 +02:00
|
|
|
if (r.contains(q->viewport()->rect()))
|
2009-03-17 16:16:36 +01:00
|
|
|
slotUpdateExtraAreaWidth();
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::saveCurrentCursorPositionForNavigation()
|
2009-03-19 14:18:26 +01:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
m_lastCursorChangeWasInteresting = true;
|
|
|
|
|
m_tempNavigationState = q->saveState();
|
2009-03-19 14:18:26 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::updateCurrentLineHighlight()
|
2009-07-15 16:10:15 +02:00
|
|
|
{
|
|
|
|
|
QList<QTextEdit::ExtraSelection> extraSelections;
|
|
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
if (m_highlightCurrentLine) {
|
2009-07-15 16:10:15 +02:00
|
|
|
QTextEdit::ExtraSelection sel;
|
2014-08-01 23:31:56 +02:00
|
|
|
sel.format.setBackground(q->textDocument()->fontSettings()
|
2014-01-21 14:29:25 +01:00
|
|
|
.toTextCharFormat(C_CURRENT_LINE).background());
|
2009-07-15 16:10:15 +02:00
|
|
|
sel.format.setProperty(QTextFormat::FullWidthSelection, true);
|
2014-07-25 16:23:50 +02:00
|
|
|
sel.cursor = q->textCursor();
|
2009-07-15 16:10:15 +02:00
|
|
|
sel.cursor.clearSelection();
|
|
|
|
|
extraSelections.append(sel);
|
|
|
|
|
}
|
2015-06-04 10:43:34 +02:00
|
|
|
updateCurrentLineInScrollbar();
|
2009-07-15 16:10:15 +02:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
q->setExtraSelections(TextEditorWidget::CurrentLineSelection, extraSelections);
|
2009-12-01 15:55:55 +01:00
|
|
|
|
|
|
|
|
// the extra area shows information for the entire current block, not just the currentline.
|
|
|
|
|
// This is why we must force a bigger update region.
|
2014-07-25 16:23:50 +02:00
|
|
|
int cursorBlockNumber = q->textCursor().blockNumber();
|
|
|
|
|
if (cursorBlockNumber != m_cursorBlockNumber) {
|
|
|
|
|
QPointF offset = q->contentOffset();
|
|
|
|
|
QTextBlock block = q->document()->findBlockByNumber(m_cursorBlockNumber);
|
2009-12-01 15:55:55 +01:00
|
|
|
if (block.isValid())
|
2014-07-25 16:23:50 +02:00
|
|
|
m_extraArea->update(q->blockBoundingGeometry(block).translated(offset).toAlignedRect());
|
|
|
|
|
block = q->document()->findBlockByNumber(cursorBlockNumber);
|
2010-09-07 15:55:06 +02:00
|
|
|
if (block.isValid() && block.isVisible())
|
2014-07-25 16:23:50 +02:00
|
|
|
m_extraArea->update(q->blockBoundingGeometry(block).translated(offset).toAlignedRect());
|
|
|
|
|
m_cursorBlockNumber = cursorBlockNumber;
|
2009-12-01 15:55:55 +01:00
|
|
|
}
|
2009-07-15 16:10:15 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::slotCursorPositionChanged()
|
2009-03-17 16:16:36 +01:00
|
|
|
{
|
2009-09-25 17:22:10 +02:00
|
|
|
#if 0
|
|
|
|
|
qDebug() << "block" << textCursor().blockNumber()+1
|
2010-05-20 15:10:26 +02:00
|
|
|
<< "brace depth:" << BaseTextDocumentLayout::braceDepth(textCursor().block())
|
|
|
|
|
<< "indent:" << BaseTextDocumentLayout::userData(textCursor().block())->foldingIndent();
|
2009-09-25 17:22:10 +02:00
|
|
|
#endif
|
2009-03-19 14:18:26 +01:00
|
|
|
if (!d->m_contentsChanged && d->m_lastCursorChangeWasInteresting) {
|
2014-09-02 09:59:56 +02:00
|
|
|
if (EditorManager::currentEditor() && EditorManager::currentEditor()->widget() == this)
|
|
|
|
|
EditorManager::addCurrentPositionToNavigationHistory(d->m_tempNavigationState);
|
2009-03-19 14:18:26 +01:00
|
|
|
d->m_lastCursorChangeWasInteresting = false;
|
|
|
|
|
} else if (d->m_contentsChanged) {
|
2014-07-25 16:23:50 +02:00
|
|
|
d->saveCurrentCursorPositionForNavigation();
|
2009-03-19 14:18:26 +01:00
|
|
|
}
|
2011-04-15 16:19:23 +02:00
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
d->updateHighlights();
|
2010-02-08 16:54:14 +01:00
|
|
|
}
|
2009-04-27 13:01:28 +02:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::updateHighlights()
|
2010-02-08 16:54:14 +01:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
if (m_parenthesesMatchingEnabled && q->hasFocus()) {
|
2009-04-27 13:01:28 +02:00
|
|
|
// Delay update when no matching is displayed yet, to avoid flicker
|
2014-09-26 11:37:54 +02:00
|
|
|
if (q->extraSelections(TextEditorWidget::ParenthesesMatchingSelection).isEmpty()
|
2016-04-28 12:51:34 +02:00
|
|
|
&& m_bracketsAnimator == 0) {
|
2014-07-25 16:23:50 +02:00
|
|
|
m_parenthesesMatchingTimer.start(50);
|
2009-04-27 13:01:28 +02:00
|
|
|
} else {
|
2012-10-11 14:46:18 +04:00
|
|
|
// when we uncheck "highlight matching parentheses"
|
|
|
|
|
// we need clear current selection before viewport update
|
|
|
|
|
// otherwise we get sticky highlighted parentheses
|
2014-07-25 16:23:50 +02:00
|
|
|
if (!m_displaySettings.m_highlightMatchingParentheses)
|
2014-09-26 11:37:54 +02:00
|
|
|
q->setExtraSelections(TextEditorWidget::ParenthesesMatchingSelection, QList<QTextEdit::ExtraSelection>());
|
2012-10-11 14:46:18 +04:00
|
|
|
|
|
|
|
|
// use 0-timer, not direct call, to give the syntax highlighter a chance
|
2012-09-05 14:33:13 +02:00
|
|
|
// to update the parentheses information
|
2014-07-25 16:23:50 +02:00
|
|
|
m_parenthesesMatchingTimer.start(0);
|
2009-04-27 13:01:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-26 08:17:29 +02:00
|
|
|
if (m_highlightAutoComplete && !m_autoCompleteHighlightPos.isEmpty()) {
|
2016-06-01 10:19:59 +02:00
|
|
|
QTimer::singleShot(0, this, [this](){
|
2017-06-26 08:17:29 +02:00
|
|
|
const QTextCursor &cursor = q->textCursor();
|
|
|
|
|
auto popAutoCompletion = [&]() {
|
|
|
|
|
return !m_autoCompleteHighlightPos.isEmpty()
|
|
|
|
|
&& m_autoCompleteHighlightPos.last() != cursor;
|
|
|
|
|
};
|
2017-07-04 10:49:04 +02:00
|
|
|
if ((!m_keepAutoCompletionHighlight && !q->hasFocus()) || popAutoCompletion()) {
|
2017-06-26 08:17:29 +02:00
|
|
|
while (popAutoCompletion())
|
|
|
|
|
m_autoCompleteHighlightPos.pop_back();
|
|
|
|
|
updateAutoCompleteHighlight();
|
2016-06-01 10:19:59 +02:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2009-07-15 16:10:15 +02:00
|
|
|
updateCurrentLineHighlight();
|
2009-04-23 17:28:53 +02:00
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
if (m_displaySettings.m_highlightBlocks) {
|
|
|
|
|
QTextCursor cursor = q->textCursor();
|
|
|
|
|
extraAreaHighlightFoldedBlockNumber = cursor.blockNumber();
|
|
|
|
|
m_highlightBlocksTimer.start(100);
|
2009-04-23 17:28:53 +02:00
|
|
|
}
|
2009-03-17 16:16:36 +01:00
|
|
|
}
|
|
|
|
|
|
2015-06-04 10:43:34 +02:00
|
|
|
void TextEditorWidgetPrivate::updateCurrentLineInScrollbar()
|
|
|
|
|
{
|
|
|
|
|
if (m_highlightCurrentLine && m_highlightScrollBar) {
|
|
|
|
|
m_highlightScrollBar->removeHighlights(Constants::SCROLL_BAR_CURRENT_LINE);
|
|
|
|
|
if (m_highlightScrollBar->maximum() > 0) {
|
|
|
|
|
const QTextCursor &tc = q->textCursor();
|
2016-03-30 14:07:51 +02:00
|
|
|
if (QTextLayout *layout = tc.block().layout()) {
|
2017-06-01 12:06:03 +02:00
|
|
|
const int pos = q->textCursor().block().firstLineNumber() +
|
2016-03-30 14:07:51 +02:00
|
|
|
layout->lineForTextPosition(tc.positionInBlock()).lineNumber();
|
2017-06-01 12:06:03 +02:00
|
|
|
m_highlightScrollBar->addHighlight({Constants::SCROLL_BAR_CURRENT_LINE, pos,
|
|
|
|
|
Theme::TextEditor_CurrentLine_ScrollBarColor,
|
|
|
|
|
Highlight::HighestPriority});
|
2016-03-30 14:07:51 +02:00
|
|
|
}
|
2015-06-04 10:43:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::slotUpdateBlockNotify(const QTextBlock &block)
|
2009-03-17 16:16:36 +01:00
|
|
|
{
|
|
|
|
|
static bool blockRecursion = false;
|
|
|
|
|
if (blockRecursion)
|
|
|
|
|
return;
|
2009-11-25 15:55:45 +01:00
|
|
|
blockRecursion = true;
|
2014-08-01 15:06:28 +02:00
|
|
|
if (m_overlay->isVisible()) {
|
2009-11-25 15:55:45 +01:00
|
|
|
/* an overlay might draw outside the block bounderies, force
|
|
|
|
|
complete viewport update */
|
2014-08-01 15:06:28 +02:00
|
|
|
q->viewport()->update();
|
2009-12-02 13:24:53 +01:00
|
|
|
} else {
|
|
|
|
|
if (block.previous().isValid() && block.userState() != block.previous().userState()) {
|
2009-03-17 16:16:36 +01:00
|
|
|
/* The syntax highlighting state changes. This opens up for
|
|
|
|
|
the possibility that the paragraph has braces that support
|
|
|
|
|
code folding. In this case, do the save thing and also
|
2010-05-20 15:10:26 +02:00
|
|
|
update the previous block, which might contain a fold
|
2009-03-17 16:16:36 +01:00
|
|
|
box which now is invalid.*/
|
2014-08-01 15:06:28 +02:00
|
|
|
emit q->requestBlockUpdate(block.previous());
|
2009-12-02 13:24:53 +01:00
|
|
|
}
|
2014-08-01 15:06:28 +02:00
|
|
|
if (!m_findScopeStart.isNull()) {
|
|
|
|
|
if (block.position() < m_findScopeEnd.position()
|
|
|
|
|
&& block.position() + block.length() >= m_findScopeStart.position()) {
|
|
|
|
|
QTextBlock b = block.document()->findBlock(m_findScopeStart.position());
|
2009-12-02 13:24:53 +01:00
|
|
|
do {
|
2014-08-01 15:06:28 +02:00
|
|
|
emit q->requestBlockUpdate(b);
|
2009-12-02 13:24:53 +01:00
|
|
|
b = b.next();
|
2014-08-01 15:06:28 +02:00
|
|
|
} while (b.isValid() && b.position() < m_findScopeEnd.position());
|
2009-12-02 13:24:53 +01:00
|
|
|
}
|
|
|
|
|
}
|
2009-03-17 16:16:36 +01:00
|
|
|
}
|
2009-11-25 15:55:45 +01:00
|
|
|
blockRecursion = false;
|
2009-03-17 16:16:36 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::timerEvent(QTimerEvent *e)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
if (e->timerId() == d->autoScrollTimer.timerId()) {
|
|
|
|
|
const QPoint globalPos = QCursor::pos();
|
|
|
|
|
const QPoint pos = d->m_extraArea->mapFromGlobal(globalPos);
|
|
|
|
|
QRect visible = d->m_extraArea->rect();
|
|
|
|
|
verticalScrollBar()->triggerAction( pos.y() < visible.center().y() ?
|
|
|
|
|
QAbstractSlider::SliderSingleStepSub
|
|
|
|
|
: QAbstractSlider::SliderSingleStepAdd);
|
|
|
|
|
QMouseEvent ev(QEvent::MouseMove, pos, globalPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
|
|
|
|
|
extraAreaMouseEvent(&ev);
|
|
|
|
|
int delta = qMax(pos.y() - visible.top(), visible.bottom() - pos.y()) - visible.height();
|
|
|
|
|
if (delta < 7)
|
|
|
|
|
delta = 7;
|
|
|
|
|
int timeout = 4900 / (delta * delta);
|
|
|
|
|
d->autoScrollTimer.start(timeout, this);
|
|
|
|
|
|
2010-05-20 15:10:26 +02:00
|
|
|
} else if (e->timerId() == d->foldedBlockTimer.timerId()) {
|
|
|
|
|
d->visibleFoldedBlockNumber = d->suggestedVisibleFoldedBlockNumber;
|
|
|
|
|
d->suggestedVisibleFoldedBlockNumber = -1;
|
|
|
|
|
d->foldedBlockTimer.stop();
|
2008-12-02 12:01:29 +01:00
|
|
|
viewport()->update();
|
2014-06-16 14:20:36 +02:00
|
|
|
} else if (e->timerId() == d->m_cursorFlashTimer.timerId()) {
|
|
|
|
|
d->m_cursorVisible = !d->m_cursorVisible;
|
|
|
|
|
viewport()->update();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
QPlainTextEdit::timerEvent(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::clearVisibleFoldedBlock()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-05-20 15:10:26 +02:00
|
|
|
if (suggestedVisibleFoldedBlockNumber) {
|
|
|
|
|
suggestedVisibleFoldedBlockNumber = -1;
|
|
|
|
|
foldedBlockTimer.stop();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2010-05-20 15:10:26 +02:00
|
|
|
if (visibleFoldedBlockNumber >= 0) {
|
|
|
|
|
visibleFoldedBlockNumber = -1;
|
2008-12-02 12:01:29 +01:00
|
|
|
q->viewport()->update();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::mouseMoveEvent(QMouseEvent *e)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2015-09-21 10:27:43 +02:00
|
|
|
d->requestUpdateLink(e);
|
2009-09-08 13:03:24 +02:00
|
|
|
|
2009-03-25 11:38:54 +01:00
|
|
|
if (e->buttons() == Qt::NoButton) {
|
2014-07-25 16:23:50 +02:00
|
|
|
const QTextBlock collapsedBlock = d->foldedBlockAt(e->pos());
|
2009-03-25 11:38:54 +01:00
|
|
|
const int blockNumber = collapsedBlock.next().blockNumber();
|
2008-12-02 12:01:29 +01:00
|
|
|
if (blockNumber < 0) {
|
2010-05-20 15:10:26 +02:00
|
|
|
d->clearVisibleFoldedBlock();
|
|
|
|
|
} else if (blockNumber != d->visibleFoldedBlockNumber) {
|
|
|
|
|
d->suggestedVisibleFoldedBlockNumber = blockNumber;
|
|
|
|
|
d->foldedBlockTimer.start(40, this);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2009-03-25 11:38:54 +01:00
|
|
|
|
2010-07-02 13:47:14 +02:00
|
|
|
const RefactorMarker refactorMarker = d->m_refactorOverlay->markerAt(e->pos());
|
|
|
|
|
|
2009-03-25 11:38:54 +01:00
|
|
|
// Update the mouse cursor
|
2010-07-02 13:47:14 +02:00
|
|
|
if ((collapsedBlock.isValid() || refactorMarker.isValid()) && !d->m_mouseOnFoldedMarker) {
|
2010-05-20 15:10:26 +02:00
|
|
|
d->m_mouseOnFoldedMarker = true;
|
2009-03-25 11:38:54 +01:00
|
|
|
viewport()->setCursor(Qt::PointingHandCursor);
|
2010-07-02 13:47:14 +02:00
|
|
|
} else if (!collapsedBlock.isValid() && !refactorMarker.isValid() && d->m_mouseOnFoldedMarker) {
|
2010-05-20 15:10:26 +02:00
|
|
|
d->m_mouseOnFoldedMarker = false;
|
2009-03-25 11:38:54 +01:00
|
|
|
viewport()->setCursor(Qt::IBeamCursor);
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
} else {
|
|
|
|
|
QPlainTextEdit::mouseMoveEvent(e);
|
2010-08-05 15:01:20 +02:00
|
|
|
|
|
|
|
|
if (e->modifiers() & Qt::AltModifier) {
|
|
|
|
|
if (!d->m_inBlockSelectionMode) {
|
2014-06-16 14:20:36 +02:00
|
|
|
if (textCursor().hasSelection()) {
|
|
|
|
|
d->enableBlockSelection(textCursor());
|
|
|
|
|
} else {
|
|
|
|
|
const QTextCursor &cursor = cursorForPosition(e->pos());
|
|
|
|
|
int column = d->m_document->tabSettings().columnAt(
|
|
|
|
|
cursor.block().text(), cursor.positionInBlock());
|
|
|
|
|
if (cursor.positionInBlock() == cursor.block().length()-1)
|
|
|
|
|
column += (e->pos().x() - cursorRect().center().x()) / QFontMetricsF(font()).width(QLatin1Char(' '));
|
|
|
|
|
int block = cursor.blockNumber();
|
|
|
|
|
if (block == blockCount() - 1)
|
|
|
|
|
block += (e->pos().y() - cursorRect().center().y()) / QFontMetricsF(font()).lineSpacing();
|
|
|
|
|
d->enableBlockSelection(block, column, block, column);
|
|
|
|
|
}
|
2010-08-05 15:01:20 +02:00
|
|
|
} else {
|
2014-06-16 14:20:36 +02:00
|
|
|
const QTextCursor &cursor = textCursor();
|
2010-08-05 15:01:20 +02:00
|
|
|
|
|
|
|
|
// get visual column
|
2014-01-17 15:54:59 +01:00
|
|
|
int column = d->m_document->tabSettings().columnAt(
|
|
|
|
|
cursor.block().text(), cursor.positionInBlock());
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
if (cursor.positionInBlock() == cursor.block().length()-1)
|
2014-06-16 14:20:36 +02:00
|
|
|
column += (e->pos().x() - cursorRect().center().x()) / QFontMetricsF(font()).width(QLatin1Char(' '));
|
|
|
|
|
|
|
|
|
|
d->m_blockSelection.positionBlock = cursor.blockNumber();
|
|
|
|
|
d->m_blockSelection.positionColumn = column;
|
|
|
|
|
|
2015-04-21 12:36:11 +02:00
|
|
|
doSetTextCursor(d->m_blockSelection.selection(d->m_document.data()), true);
|
2010-08-05 15:01:20 +02:00
|
|
|
viewport()->update();
|
|
|
|
|
}
|
2014-06-16 14:20:36 +02:00
|
|
|
} else if (d->m_inBlockSelectionMode) {
|
2016-12-21 08:44:10 +01:00
|
|
|
d->disableBlockSelection(TextEditorWidgetPrivate::CursorUpdateKeepSelection);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
2009-06-12 12:38:27 +02:00
|
|
|
if (viewport()->cursor().shape() == Qt::BlankCursor)
|
|
|
|
|
viewport()->setCursor(Qt::IBeamCursor);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2010-03-19 16:23:10 +01:00
|
|
|
static bool handleForwardBackwardMouseButtons(QMouseEvent *e)
|
|
|
|
|
{
|
|
|
|
|
if (e->button() == Qt::XButton1) {
|
2014-08-19 14:23:03 +02:00
|
|
|
EditorManager::goBackInNavigationHistory();
|
2010-03-19 16:23:10 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (e->button() == Qt::XButton2) {
|
2014-08-19 14:23:03 +02:00
|
|
|
EditorManager::goForwardInNavigationHistory();
|
2010-03-19 16:23:10 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::mousePressEvent(QMouseEvent *e)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
if (e->button() == Qt::LeftButton) {
|
2014-06-16 14:20:36 +02:00
|
|
|
if (e->modifiers() == Qt::AltModifier) {
|
|
|
|
|
const QTextCursor &cursor = cursorForPosition(e->pos());
|
|
|
|
|
int column = d->m_document->tabSettings().columnAt(
|
|
|
|
|
cursor.block().text(), cursor.positionInBlock());
|
|
|
|
|
if (cursor.positionInBlock() == cursor.block().length()-1)
|
|
|
|
|
column += (e->pos().x() - cursorRect(cursor).center().x()) / QFontMetricsF(font()).width(QLatin1Char(' '));
|
|
|
|
|
int block = cursor.blockNumber();
|
|
|
|
|
if (block == blockCount() - 1)
|
|
|
|
|
block += (e->pos().y() - cursorRect(cursor).center().y()) / QFontMetricsF(font()).lineSpacing();
|
|
|
|
|
if (d->m_inBlockSelectionMode) {
|
|
|
|
|
d->m_blockSelection.positionBlock = block;
|
|
|
|
|
d->m_blockSelection.positionColumn = column;
|
2009-11-03 14:29:49 +01:00
|
|
|
|
2015-04-21 12:36:11 +02:00
|
|
|
doSetTextCursor(d->m_blockSelection.selection(d->m_document.data()), true);
|
2014-06-16 14:20:36 +02:00
|
|
|
viewport()->update();
|
|
|
|
|
} else {
|
|
|
|
|
d->enableBlockSelection(block, column, block, column);
|
|
|
|
|
}
|
2010-07-02 13:47:14 +02:00
|
|
|
} else {
|
2014-06-16 14:20:36 +02:00
|
|
|
if (d->m_inBlockSelectionMode)
|
2016-12-21 08:44:10 +01:00
|
|
|
d->disableBlockSelection(TextEditorWidgetPrivate::NoCursorUpdate);
|
2014-06-16 14:20:36 +02:00
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
QTextBlock foldedBlock = d->foldedBlockAt(e->pos());
|
2014-06-16 14:20:36 +02:00
|
|
|
if (foldedBlock.isValid()) {
|
2014-07-25 16:23:50 +02:00
|
|
|
d->toggleBlockVisible(foldedBlock);
|
2014-06-16 14:20:36 +02:00
|
|
|
viewport()->setCursor(Qt::IBeamCursor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RefactorMarker refactorMarker = d->m_refactorOverlay->markerAt(e->pos());
|
|
|
|
|
if (refactorMarker.isValid()) {
|
2014-08-20 21:40:21 +02:00
|
|
|
onRefactorMarkerClicked(refactorMarker);
|
2014-06-16 14:20:36 +02:00
|
|
|
} else {
|
2015-09-21 10:27:43 +02:00
|
|
|
d->requestUpdateLink(e, true);
|
2009-11-03 14:29:49 +01:00
|
|
|
|
2014-06-16 14:20:36 +02:00
|
|
|
if (d->m_currentLink.hasValidLinkText())
|
|
|
|
|
d->m_linkPressed = true;
|
|
|
|
|
}
|
2010-07-02 13:47:14 +02:00
|
|
|
}
|
2012-02-08 13:09:04 +01:00
|
|
|
} else if (e->button() == Qt::RightButton) {
|
|
|
|
|
int eventCursorPosition = cursorForPosition(e->pos()).position();
|
|
|
|
|
if (eventCursorPosition < textCursor().selectionStart()
|
|
|
|
|
|| eventCursorPosition > textCursor().selectionEnd()) {
|
|
|
|
|
setTextCursor(cursorForPosition(e->pos()));
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2010-03-19 16:23:10 +01:00
|
|
|
|
2012-08-23 15:53:58 +02:00
|
|
|
if (HostOsInfo::isLinuxHost() && handleForwardBackwardMouseButtons(e))
|
2010-03-19 16:23:10 +01:00
|
|
|
return;
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
QPlainTextEdit::mousePressEvent(e);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::mouseReleaseEvent(QMouseEvent *e)
|
2009-09-08 13:03:24 +02:00
|
|
|
{
|
2009-12-09 16:27:34 +01:00
|
|
|
if (mouseNavigationEnabled()
|
2012-11-01 03:43:28 +01:00
|
|
|
&& d->m_linkPressed
|
|
|
|
|
&& e->modifiers() & Qt::ControlModifier
|
|
|
|
|
&& !(e->modifiers() & Qt::ShiftModifier)
|
|
|
|
|
&& e->button() == Qt::LeftButton
|
|
|
|
|
) {
|
|
|
|
|
|
2014-08-19 14:23:03 +02:00
|
|
|
EditorManager::addCurrentPositionToNavigationHistory();
|
2013-02-05 14:14:33 +01:00
|
|
|
bool inNextSplit = ((e->modifiers() & Qt::AltModifier) && !alwaysOpenLinksInNextSplit())
|
|
|
|
|
|| (alwaysOpenLinksInNextSplit() && !(e->modifiers() & Qt::AltModifier));
|
|
|
|
|
if (openLink(findLinkAt(cursorForPosition(e->pos())), inNextSplit)) {
|
2014-07-25 16:23:50 +02:00
|
|
|
d->clearLink();
|
2009-09-08 13:03:24 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-08-23 15:53:58 +02:00
|
|
|
if (!HostOsInfo::isLinuxHost() && handleForwardBackwardMouseButtons(e))
|
2010-03-19 16:23:10 +01:00
|
|
|
return;
|
|
|
|
|
|
2009-09-08 13:03:24 +02:00
|
|
|
QPlainTextEdit::mouseReleaseEvent(e);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::mouseDoubleClickEvent(QMouseEvent *e)
|
2011-10-29 13:09:33 +08:00
|
|
|
{
|
2012-03-01 20:47:34 +02:00
|
|
|
if (e->button() == Qt::LeftButton) {
|
|
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
|
const int position = cursor.position();
|
|
|
|
|
if (TextBlockUserData::findPreviousOpenParenthesis(&cursor, false, true)) {
|
|
|
|
|
if (position - cursor.position() == 1 && selectBlockUp())
|
|
|
|
|
return;
|
|
|
|
|
}
|
2011-10-29 13:09:33 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QPlainTextEdit::mouseDoubleClickEvent(e);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::leaveEvent(QEvent *e)
|
2009-09-08 13:03:24 +02:00
|
|
|
{
|
|
|
|
|
// Clear link emulation when the mouse leaves the editor
|
2014-07-25 16:23:50 +02:00
|
|
|
d->clearLink();
|
2009-09-08 13:03:24 +02:00
|
|
|
QPlainTextEdit::leaveEvent(e);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::keyReleaseEvent(QKeyEvent *e)
|
2009-09-08 13:03:24 +02:00
|
|
|
{
|
2011-07-20 13:40:24 +02:00
|
|
|
if (e->key() == Qt::Key_Control) {
|
2014-07-25 16:23:50 +02:00
|
|
|
d->clearLink();
|
2011-07-20 13:40:24 +02:00
|
|
|
} else if (e->key() == Qt::Key_Shift
|
2011-12-08 13:23:41 +01:00
|
|
|
&& d->m_behaviorSettings.m_constrainHoverTooltips
|
2013-09-11 17:11:15 +02:00
|
|
|
&& ToolTip::isVisible()) {
|
|
|
|
|
ToolTip::hide();
|
2011-12-08 13:23:41 +01:00
|
|
|
} else if (e->key() == Qt::Key_Alt
|
|
|
|
|
&& d->m_maybeFakeTooltipEvent) {
|
|
|
|
|
d->m_maybeFakeTooltipEvent = false;
|
2014-07-25 16:23:50 +02:00
|
|
|
d->processTooltipRequest(textCursor());
|
2011-07-20 13:40:24 +02:00
|
|
|
}
|
2009-09-08 13:03:24 +02:00
|
|
|
|
|
|
|
|
QPlainTextEdit::keyReleaseEvent(e);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::dragEnterEvent(QDragEnterEvent *e)
|
2010-02-19 11:25:30 +01:00
|
|
|
{
|
|
|
|
|
// If the drag event contains URLs, we don't want to insert them as text
|
|
|
|
|
if (e->mimeData()->hasUrls()) {
|
|
|
|
|
e->ignore();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QPlainTextEdit::dragEnterEvent(e);
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-19 14:23:03 +02:00
|
|
|
static void appendMenuActionsFromContext(QMenu *menu, Id menuContextId)
|
2014-01-13 15:37:18 +01:00
|
|
|
{
|
2014-08-19 14:23:03 +02:00
|
|
|
ActionContainer *mcontext = ActionManager::actionContainer(menuContextId);
|
2014-01-13 15:37:18 +01:00
|
|
|
QMenu *contextMenu = mcontext->menu();
|
|
|
|
|
|
|
|
|
|
foreach (QAction *action, contextMenu->actions())
|
|
|
|
|
menu->addAction(action);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::showDefaultContextMenu(QContextMenuEvent *e, Id menuContextId)
|
2012-07-24 23:30:32 +04:00
|
|
|
{
|
|
|
|
|
QMenu menu;
|
|
|
|
|
appendMenuActionsFromContext(&menu, menuContextId);
|
|
|
|
|
appendStandardContextMenuActions(&menu);
|
|
|
|
|
menu.exec(e->globalPos());
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-19 10:43:47 +02:00
|
|
|
void TextEditorWidget::addHoverHandler(BaseHoverHandler *handler)
|
|
|
|
|
{
|
|
|
|
|
d->m_hoverHandlers.append(handler);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::extraAreaLeaveEvent(QEvent *)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2016-04-14 14:46:05 +02:00
|
|
|
d->extraAreaPreviousMarkTooltipRequestedLine = -1;
|
2016-07-22 08:57:32 +02:00
|
|
|
ToolTip::hide();
|
2016-04-14 14:46:05 +02:00
|
|
|
|
2009-04-24 16:44:48 +02:00
|
|
|
// fake missing mouse move event from Qt
|
|
|
|
|
QMouseEvent me(QEvent::MouseMove, QPoint(-1, -1), Qt::NoButton, 0, 0);
|
|
|
|
|
extraAreaMouseEvent(&me);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::extraAreaContextMenuEvent(QContextMenuEvent *e)
|
2011-08-01 12:20:08 +02:00
|
|
|
{
|
|
|
|
|
if (d->m_marksVisible) {
|
2014-09-01 14:11:39 +02:00
|
|
|
QTextCursor cursor = cursorForPosition(QPoint(0, e->pos().y()));
|
2011-08-01 12:20:08 +02:00
|
|
|
QMenu * contextMenu = new QMenu(this);
|
2014-09-30 16:54:26 +02:00
|
|
|
emit markContextMenuRequested(this, cursor.blockNumber() + 1, contextMenu);
|
2011-08-01 12:20:08 +02:00
|
|
|
if (!contextMenu->isEmpty())
|
|
|
|
|
contextMenu->exec(e->globalPos());
|
|
|
|
|
delete contextMenu;
|
|
|
|
|
e->accept();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::updateFoldingHighlight(const QPoint &pos)
|
2013-04-10 13:51:04 +02:00
|
|
|
{
|
|
|
|
|
if (!d->m_codeFoldingVisible)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
QTextCursor cursor = cursorForPosition(QPoint(0, pos.y()));
|
|
|
|
|
|
|
|
|
|
// Update which folder marker is highlighted
|
|
|
|
|
const int highlightBlockNumber = d->extraAreaHighlightFoldedBlockNumber;
|
|
|
|
|
d->extraAreaHighlightFoldedBlockNumber = -1;
|
|
|
|
|
|
|
|
|
|
if (pos.x() > extraArea()->width() - foldBoxWidth(fontMetrics())) {
|
|
|
|
|
d->extraAreaHighlightFoldedBlockNumber = cursor.blockNumber();
|
|
|
|
|
} else if (d->m_displaySettings.m_highlightBlocks) {
|
|
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
|
d->extraAreaHighlightFoldedBlockNumber = cursor.blockNumber();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (highlightBlockNumber != d->extraAreaHighlightFoldedBlockNumber)
|
2013-05-24 12:53:35 +02:00
|
|
|
d->m_highlightBlocksTimer.start(d->m_highlightBlocksInfo.isEmpty() ? 120 : 0);
|
2013-04-10 13:51:04 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::extraAreaMouseEvent(QMouseEvent *e)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
QTextCursor cursor = cursorForPosition(QPoint(0, e->pos().y()));
|
|
|
|
|
|
|
|
|
|
int markWidth;
|
|
|
|
|
extraAreaWidth(&markWidth);
|
2012-10-09 22:32:44 +02:00
|
|
|
const bool inMarkArea = e->pos().x() <= markWidth && e->pos().x() >= 0;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-04-24 16:44:48 +02:00
|
|
|
if (d->m_codeFoldingVisible
|
2013-04-10 13:51:04 +02:00
|
|
|
&& e->type() == QEvent::MouseMove && e->buttons() == 0) { // mouse tracking
|
|
|
|
|
updateFoldingHighlight(e->pos());
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-06-16 12:15:33 +02:00
|
|
|
// Set whether the mouse cursor is a hand or normal arrow
|
|
|
|
|
if (e->type() == QEvent::MouseMove) {
|
2012-10-09 22:32:44 +02:00
|
|
|
if (inMarkArea) {
|
2012-09-20 11:27:22 +04:00
|
|
|
int line = cursor.blockNumber() + 1;
|
2016-07-19 11:58:15 +02:00
|
|
|
if (d->extraAreaPreviousMarkTooltipRequestedLine != line) {
|
|
|
|
|
if (auto data = static_cast<TextBlockUserData *>(cursor.block().userData())) {
|
2016-07-22 08:57:32 +02:00
|
|
|
if (data->marks().isEmpty()) {
|
|
|
|
|
ToolTip::hide();
|
|
|
|
|
} else {
|
2016-08-16 14:19:41 +02:00
|
|
|
auto layout = new QGridLayout;
|
2016-07-22 08:57:32 +02:00
|
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
|
|
|
|
layout->setSpacing(2);
|
|
|
|
|
foreach (TextMark *mark, data->marks())
|
|
|
|
|
mark->addToToolTipLayout(layout);
|
|
|
|
|
ToolTip::show(mapToGlobal(e->pos()), layout, this);
|
2016-07-19 11:58:15 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-04-14 14:46:05 +02:00
|
|
|
d->extraAreaPreviousMarkTooltipRequestedLine = line;
|
2012-09-20 11:27:22 +04:00
|
|
|
}
|
|
|
|
|
|
2012-11-14 04:08:31 +01:00
|
|
|
if (e->buttons() & Qt::LeftButton && !d->m_markDragStart.isNull()) {
|
2012-10-09 22:32:44 +02:00
|
|
|
int dist = (e->pos() - d->m_markDragStart).manhattanLength();
|
|
|
|
|
if (dist > QApplication::startDragDistance())
|
|
|
|
|
d->m_markDragging = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (d->m_markDragging)
|
|
|
|
|
d->m_extraArea->setCursor(inMarkArea ? Qt::DragMoveCursor : Qt::ForbiddenCursor);
|
|
|
|
|
else if (inMarkArea != (d->m_extraArea->cursor().shape() == Qt::PointingHandCursor))
|
|
|
|
|
d->m_extraArea->setCursor(inMarkArea ? Qt::PointingHandCursor : Qt::ArrowCursor);
|
2009-06-16 12:15:33 +02:00
|
|
|
}
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
if (e->type() == QEvent::MouseButtonPress || e->type() == QEvent::MouseButtonDblClick) {
|
|
|
|
|
if (e->button() == Qt::LeftButton) {
|
2010-05-20 15:10:26 +02:00
|
|
|
int boxWidth = foldBoxWidth(fontMetrics());
|
2009-06-05 16:16:03 +02:00
|
|
|
if (d->m_codeFoldingVisible && e->pos().x() > extraArea()->width() - boxWidth) {
|
2009-04-24 16:44:48 +02:00
|
|
|
if (!cursor.block().next().isVisible()) {
|
2014-07-25 16:23:50 +02:00
|
|
|
d->toggleBlockVisible(cursor.block());
|
2009-04-24 16:44:48 +02:00
|
|
|
d->moveCursorVisible(false);
|
2014-07-25 16:23:50 +02:00
|
|
|
} else if (d->foldBox().contains(e->pos())) {
|
2009-04-24 16:44:48 +02:00
|
|
|
cursor.setPosition(
|
|
|
|
|
document()->findBlockByNumber(d->m_highlightBlocksInfo.open.last()).position()
|
|
|
|
|
);
|
|
|
|
|
QTextBlock c = cursor.block();
|
2014-07-25 16:23:50 +02:00
|
|
|
d->toggleBlockVisible(c);
|
2009-04-24 16:44:48 +02:00
|
|
|
d->moveCursorVisible(false);
|
|
|
|
|
}
|
2012-10-09 22:32:44 +02:00
|
|
|
} else if (d->m_lineNumbersVisible && !inMarkArea) {
|
2008-12-02 12:01:29 +01:00
|
|
|
QTextCursor selection = cursor;
|
|
|
|
|
selection.setVisualNavigation(true);
|
|
|
|
|
d->extraAreaSelectionAnchorBlockNumber = selection.blockNumber();
|
|
|
|
|
selection.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
|
|
|
|
selection.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
|
|
|
|
|
setTextCursor(selection);
|
|
|
|
|
} else {
|
|
|
|
|
d->extraAreaToggleMarkBlockNumber = cursor.blockNumber();
|
2012-10-09 22:32:44 +02:00
|
|
|
d->m_markDragging = false;
|
2012-11-14 04:08:31 +01:00
|
|
|
QTextBlock block = cursor.document()->findBlockByNumber(d->extraAreaToggleMarkBlockNumber);
|
|
|
|
|
if (TextBlockUserData *data = static_cast<TextBlockUserData *>(block.userData())) {
|
|
|
|
|
TextMarks marks = data->marks();
|
|
|
|
|
for (int i = marks.size(); --i >= 0; ) {
|
2014-07-19 11:27:28 +02:00
|
|
|
TextMark *mark = marks.at(i);
|
2012-11-14 04:08:31 +01:00
|
|
|
if (mark->isDraggable()) {
|
|
|
|
|
d->m_markDragStart = e->pos();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (d->extraAreaSelectionAnchorBlockNumber >= 0) {
|
|
|
|
|
QTextCursor selection = cursor;
|
|
|
|
|
selection.setVisualNavigation(true);
|
|
|
|
|
if (e->type() == QEvent::MouseMove) {
|
|
|
|
|
QTextBlock anchorBlock = document()->findBlockByNumber(d->extraAreaSelectionAnchorBlockNumber);
|
|
|
|
|
selection.setPosition(anchorBlock.position());
|
|
|
|
|
if (cursor.blockNumber() < d->extraAreaSelectionAnchorBlockNumber) {
|
|
|
|
|
selection.movePosition(QTextCursor::EndOfBlock);
|
|
|
|
|
selection.movePosition(QTextCursor::Right);
|
|
|
|
|
}
|
|
|
|
|
selection.setPosition(cursor.block().position(), QTextCursor::KeepAnchor);
|
|
|
|
|
if (cursor.blockNumber() >= d->extraAreaSelectionAnchorBlockNumber) {
|
|
|
|
|
selection.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
|
|
|
|
selection.movePosition(QTextCursor::Right, QTextCursor::KeepAnchor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (e->pos().y() >= 0 && e->pos().y() <= d->m_extraArea->height())
|
|
|
|
|
d->autoScrollTimer.stop();
|
|
|
|
|
else if (!d->autoScrollTimer.isActive())
|
|
|
|
|
d->autoScrollTimer.start(100, this);
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
d->autoScrollTimer.stop();
|
|
|
|
|
d->extraAreaSelectionAnchorBlockNumber = -1;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
setTextCursor(selection);
|
|
|
|
|
} else if (d->extraAreaToggleMarkBlockNumber >= 0 && d->m_marksVisible && d->m_requestMarkEnabled) {
|
|
|
|
|
if (e->type() == QEvent::MouseButtonRelease && e->button() == Qt::LeftButton) {
|
|
|
|
|
int n = d->extraAreaToggleMarkBlockNumber;
|
|
|
|
|
d->extraAreaToggleMarkBlockNumber = -1;
|
2012-10-09 22:32:44 +02:00
|
|
|
const bool sameLine = cursor.blockNumber() == n;
|
|
|
|
|
const bool wasDragging = d->m_markDragging;
|
|
|
|
|
d->m_markDragging = false;
|
2012-11-14 04:08:31 +01:00
|
|
|
d->m_markDragStart = QPoint();
|
2012-10-09 22:32:44 +02:00
|
|
|
QTextBlock block = cursor.document()->findBlockByNumber(n);
|
|
|
|
|
if (TextBlockUserData *data = static_cast<TextBlockUserData *>(block.userData())) {
|
2012-10-17 15:10:38 +02:00
|
|
|
TextMarks marks = data->marks();
|
|
|
|
|
for (int i = marks.size(); --i >= 0; ) {
|
2014-07-19 11:27:28 +02:00
|
|
|
TextMark *mark = marks.at(i);
|
2012-10-09 22:32:44 +02:00
|
|
|
if (sameLine) {
|
2012-10-09 14:02:52 +02:00
|
|
|
if (mark->isClickable()) {
|
2012-02-29 16:15:25 +01:00
|
|
|
mark->clicked();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2012-10-09 22:32:44 +02:00
|
|
|
} else {
|
|
|
|
|
if (wasDragging && mark->isDraggable()) {
|
|
|
|
|
if (inMarkArea) {
|
|
|
|
|
mark->dragToLine(cursor.blockNumber() + 1);
|
|
|
|
|
d->m_extraArea->setCursor(Qt::PointingHandCursor);
|
|
|
|
|
} else {
|
|
|
|
|
d->m_extraArea->setCursor(Qt::ArrowCursor);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
2012-02-29 16:15:25 +01:00
|
|
|
}
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2012-10-09 22:32:44 +02:00
|
|
|
int line = n + 1;
|
2014-09-30 17:45:48 +02:00
|
|
|
TextMarkRequestKind kind;
|
2012-10-09 22:32:44 +02:00
|
|
|
if (QApplication::keyboardModifiers() & Qt::ShiftModifier)
|
2014-09-30 17:45:48 +02:00
|
|
|
kind = BookmarkRequest;
|
2012-10-09 22:32:44 +02:00
|
|
|
else
|
2014-09-30 17:45:48 +02:00
|
|
|
kind = BreakpointRequest;
|
2012-10-09 22:32:44 +02:00
|
|
|
|
2014-09-30 16:54:26 +02:00
|
|
|
emit markRequested(this, line, kind);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::ensureCursorVisible()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2017-05-23 14:34:22 +02:00
|
|
|
ensureBlockIsUnfolded(textCursor().block());
|
|
|
|
|
QPlainTextEdit::ensureCursorVisible();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TextEditorWidget::ensureBlockIsUnfolded(QTextBlock block)
|
|
|
|
|
{
|
2008-12-02 12:01:29 +01:00
|
|
|
if (!block.isVisible()) {
|
2014-09-26 09:14:03 +02:00
|
|
|
TextDocumentLayout *documentLayout = qobject_cast<TextDocumentLayout*>(document()->documentLayout());
|
2012-10-07 09:59:26 +02:00
|
|
|
QTC_ASSERT(documentLayout, return);
|
|
|
|
|
|
|
|
|
|
// Open all parent folds of current line.
|
2014-09-26 09:14:03 +02:00
|
|
|
int indent = TextDocumentLayout::foldingIndent(block);
|
2012-10-07 09:59:26 +02:00
|
|
|
block = block.previous();
|
|
|
|
|
while (block.isValid()) {
|
2014-09-26 09:14:03 +02:00
|
|
|
const int indent2 = TextDocumentLayout::foldingIndent(block);
|
|
|
|
|
if (TextDocumentLayout::canFold(block) && indent2 < indent) {
|
|
|
|
|
TextDocumentLayout::doFoldOrUnfold(block, /* unfold = */ true);
|
2012-10-07 09:59:26 +02:00
|
|
|
if (block.isVisible())
|
|
|
|
|
break;
|
|
|
|
|
indent = indent2;
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
block = block.previous();
|
2012-10-07 09:59:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
documentLayout->requestUpdate();
|
|
|
|
|
documentLayout->emitDocumentSizeChanged();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::toggleBlockVisible(const QTextBlock &block)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-09-26 09:14:03 +02:00
|
|
|
auto documentLayout = qobject_cast<TextDocumentLayout*>(q->document()->documentLayout());
|
2008-12-09 15:25:01 +01:00
|
|
|
QTC_ASSERT(documentLayout, return);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2014-09-26 09:14:03 +02:00
|
|
|
TextDocumentLayout::doFoldOrUnfold(block, TextDocumentLayout::isFolded(block));
|
2008-12-02 12:01:29 +01:00
|
|
|
documentLayout->requestUpdate();
|
|
|
|
|
documentLayout->emitDocumentSizeChanged();
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setLanguageSettingsId(Id settingsId)
|
2011-02-03 15:48:14 +01:00
|
|
|
{
|
|
|
|
|
d->m_tabSettingsId = settingsId;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
Id TextEditorWidget::languageSettingsId() const
|
2011-02-03 15:48:14 +01:00
|
|
|
{
|
|
|
|
|
return d->m_tabSettingsId;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setCodeStyle(ICodeStylePreferences *preferences)
|
2011-02-03 15:48:14 +01:00
|
|
|
{
|
2014-08-01 23:31:56 +02:00
|
|
|
textDocument()->indenter()->setCodeStylePreferences(preferences);
|
2011-02-03 15:48:14 +01:00
|
|
|
if (d->m_codeStylePreferences) {
|
2015-12-13 01:18:33 +02:00
|
|
|
disconnect(d->m_codeStylePreferences, &ICodeStylePreferences::currentTabSettingsChanged,
|
|
|
|
|
d->m_document.data(), &TextDocument::setTabSettings);
|
|
|
|
|
disconnect(d->m_codeStylePreferences, &ICodeStylePreferences::currentValueChanged,
|
|
|
|
|
this, &TextEditorWidget::slotCodeStyleSettingsChanged);
|
2011-02-03 15:48:14 +01:00
|
|
|
}
|
|
|
|
|
d->m_codeStylePreferences = preferences;
|
|
|
|
|
if (d->m_codeStylePreferences) {
|
2015-12-13 01:18:33 +02:00
|
|
|
connect(d->m_codeStylePreferences, &ICodeStylePreferences::currentTabSettingsChanged,
|
|
|
|
|
d->m_document.data(), &TextDocument::setTabSettings);
|
|
|
|
|
connect(d->m_codeStylePreferences, &ICodeStylePreferences::currentValueChanged,
|
|
|
|
|
this, &TextEditorWidget::slotCodeStyleSettingsChanged);
|
2014-01-17 17:09:15 +01:00
|
|
|
d->m_document->setTabSettings(d->m_codeStylePreferences->currentTabSettings());
|
2011-02-03 15:48:14 +01:00
|
|
|
slotCodeStyleSettingsChanged(d->m_codeStylePreferences->currentValue());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::slotCodeStyleSettingsChanged(const QVariant &)
|
2011-02-03 15:48:14 +01:00
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
const DisplaySettings &TextEditorWidget::displaySettings() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
return d->m_displaySettings;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
const MarginSettings &TextEditorWidget::marginSettings() const
|
2014-01-09 14:44:25 +01:00
|
|
|
{
|
|
|
|
|
return d->m_marginSettings;
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2015-12-20 19:59:00 +01:00
|
|
|
const BehaviorSettings &TextEditorWidget::behaviorSettings() const
|
|
|
|
|
{
|
|
|
|
|
return d->m_behaviorSettings;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::handleHomeKey(bool anchor)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
QTextCursor cursor = q->textCursor();
|
2008-12-02 12:01:29 +01:00
|
|
|
QTextCursor::MoveMode mode = QTextCursor::MoveAnchor;
|
|
|
|
|
|
|
|
|
|
if (anchor)
|
|
|
|
|
mode = QTextCursor::KeepAnchor;
|
|
|
|
|
|
|
|
|
|
const int initpos = cursor.position();
|
|
|
|
|
int pos = cursor.block().position();
|
2014-07-25 16:23:50 +02:00
|
|
|
QChar character = q->document()->characterAt(pos);
|
2008-12-02 12:01:29 +01:00
|
|
|
const QLatin1Char tab = QLatin1Char('\t');
|
|
|
|
|
|
|
|
|
|
while (character == tab || character.category() == QChar::Separator_Space) {
|
|
|
|
|
++pos;
|
2008-12-11 14:11:28 +01:00
|
|
|
if (pos == initpos)
|
|
|
|
|
break;
|
2014-07-25 16:23:50 +02:00
|
|
|
character = q->document()->characterAt(pos);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Go to the start of the block when we're already at the start of the text
|
|
|
|
|
if (pos == initpos)
|
|
|
|
|
pos = cursor.block().position();
|
|
|
|
|
|
|
|
|
|
cursor.setPosition(pos, mode);
|
2014-07-25 16:23:50 +02:00
|
|
|
q->setTextCursor(cursor);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::handleBackspaceKey()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
QTextCursor cursor = q->textCursor();
|
2008-12-09 15:25:01 +01:00
|
|
|
QTC_ASSERT(!cursor.hasSelection(), return);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2011-09-22 15:17:08 +02:00
|
|
|
const int pos = cursor.position();
|
|
|
|
|
if (!pos)
|
|
|
|
|
return;
|
|
|
|
|
|
2010-11-02 15:35:42 +01:00
|
|
|
bool cursorWithinSnippet = false;
|
2014-07-25 16:23:50 +02:00
|
|
|
if (m_snippetOverlay->isVisible()) {
|
2010-01-19 12:53:53 +01:00
|
|
|
QTextCursor snippetCursor = cursor;
|
|
|
|
|
snippetCursor.movePosition(QTextCursor::Left);
|
2014-07-25 16:23:50 +02:00
|
|
|
cursorWithinSnippet = snippetCheckCursor(snippetCursor);
|
2010-01-19 12:53:53 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-30 17:45:48 +02:00
|
|
|
const TabSettings &tabSettings = m_document->tabSettings();
|
|
|
|
|
const TypingSettings &typingSettings = m_document->typingSettings();
|
2009-09-16 15:39:51 +02:00
|
|
|
|
2017-06-26 08:17:29 +02:00
|
|
|
if (typingSettings.m_autoIndent
|
|
|
|
|
&& !m_autoCompleteHighlightPos.isEmpty()
|
|
|
|
|
&& (m_autoCompleteHighlightPos.last() == cursor)
|
|
|
|
|
&& m_removeAutoCompletedText
|
|
|
|
|
&& m_autoCompleter->autoBackspace(cursor)) {
|
2009-09-17 12:36:40 +02:00
|
|
|
return;
|
2016-06-14 08:00:32 +02:00
|
|
|
}
|
2009-09-16 15:39:51 +02:00
|
|
|
|
2010-11-02 15:35:42 +01:00
|
|
|
bool handled = false;
|
2011-08-16 10:45:23 +02:00
|
|
|
if (typingSettings.m_smartBackspaceBehavior == TypingSettings::BackspaceNeverIndents) {
|
2010-11-02 15:35:42 +01:00
|
|
|
if (cursorWithinSnippet)
|
|
|
|
|
cursor.beginEditBlock();
|
2009-09-16 15:39:51 +02:00
|
|
|
cursor.deletePreviousChar();
|
2010-11-02 15:35:42 +01:00
|
|
|
handled = true;
|
2011-08-16 10:45:23 +02:00
|
|
|
} else if (typingSettings.m_smartBackspaceBehavior == TypingSettings::BackspaceFollowsPreviousIndents) {
|
2010-11-02 15:35:42 +01:00
|
|
|
QTextBlock currentBlock = cursor.block();
|
|
|
|
|
int positionInBlock = pos - currentBlock.position();
|
|
|
|
|
const QString blockText = currentBlock.text();
|
|
|
|
|
if (cursor.atBlockStart() || tabSettings.firstNonSpace(blockText) < positionInBlock) {
|
|
|
|
|
if (cursorWithinSnippet)
|
|
|
|
|
cursor.beginEditBlock();
|
|
|
|
|
cursor.deletePreviousChar();
|
|
|
|
|
handled = true;
|
|
|
|
|
} else {
|
2011-08-17 12:31:15 +02:00
|
|
|
if (cursorWithinSnippet) {
|
2014-07-25 16:23:50 +02:00
|
|
|
m_snippetOverlay->mangle();
|
|
|
|
|
m_snippetOverlay->clear();
|
2011-08-17 12:31:15 +02:00
|
|
|
cursorWithinSnippet = false;
|
|
|
|
|
}
|
2010-11-02 15:35:42 +01:00
|
|
|
int previousIndent = 0;
|
|
|
|
|
const int indent = tabSettings.columnAt(blockText, positionInBlock);
|
|
|
|
|
for (QTextBlock previousNonEmptyBlock = currentBlock.previous();
|
|
|
|
|
previousNonEmptyBlock.isValid();
|
|
|
|
|
previousNonEmptyBlock = previousNonEmptyBlock.previous()) {
|
|
|
|
|
QString previousNonEmptyBlockText = previousNonEmptyBlock.text();
|
|
|
|
|
if (previousNonEmptyBlockText.trimmed().isEmpty())
|
|
|
|
|
continue;
|
|
|
|
|
previousIndent =
|
2011-08-17 12:31:15 +02:00
|
|
|
tabSettings.columnAt(previousNonEmptyBlockText,
|
|
|
|
|
tabSettings.firstNonSpace(previousNonEmptyBlockText));
|
2010-11-02 15:35:42 +01:00
|
|
|
if (previousIndent < indent) {
|
|
|
|
|
cursor.beginEditBlock();
|
|
|
|
|
cursor.setPosition(currentBlock.position(), QTextCursor::KeepAnchor);
|
|
|
|
|
cursor.insertText(tabSettings.indentationString(previousNonEmptyBlockText));
|
|
|
|
|
cursor.endEditBlock();
|
|
|
|
|
handled = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-08-16 10:45:23 +02:00
|
|
|
} else if (typingSettings.m_smartBackspaceBehavior == TypingSettings::BackspaceUnindents) {
|
2014-07-25 16:23:50 +02:00
|
|
|
const QChar c = q->document()->characterAt(pos - 1);
|
2011-09-22 15:17:08 +02:00
|
|
|
if (!(c == QLatin1Char(' ') || c == QLatin1Char('\t'))) {
|
2011-08-17 12:31:15 +02:00
|
|
|
if (cursorWithinSnippet)
|
|
|
|
|
cursor.beginEditBlock();
|
|
|
|
|
cursor.deletePreviousChar();
|
|
|
|
|
} else {
|
|
|
|
|
if (cursorWithinSnippet) {
|
2014-07-25 16:23:50 +02:00
|
|
|
m_snippetOverlay->mangle();
|
|
|
|
|
m_snippetOverlay->clear();
|
2011-08-17 12:31:15 +02:00
|
|
|
cursorWithinSnippet = false;
|
|
|
|
|
}
|
2014-07-25 16:23:50 +02:00
|
|
|
q->unindent();
|
2011-08-17 12:31:15 +02:00
|
|
|
}
|
|
|
|
|
handled = true;
|
2009-09-16 15:39:51 +02:00
|
|
|
}
|
|
|
|
|
|
2010-11-02 15:35:42 +01:00
|
|
|
if (!handled) {
|
|
|
|
|
if (cursorWithinSnippet)
|
|
|
|
|
cursor.beginEditBlock();
|
2008-12-02 12:01:29 +01:00
|
|
|
cursor.deletePreviousChar();
|
|
|
|
|
}
|
|
|
|
|
|
2010-11-02 15:35:42 +01:00
|
|
|
if (cursorWithinSnippet) {
|
|
|
|
|
cursor.endEditBlock();
|
2014-07-25 16:23:50 +02:00
|
|
|
m_snippetOverlay->updateEquivalentSelections(cursor);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2010-11-02 15:35:42 +01:00
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
q->setTextCursor(cursor);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::wheelEvent(QWheelEvent *e)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-05-20 15:10:26 +02:00
|
|
|
d->clearVisibleFoldedBlock();
|
2015-06-22 18:21:05 +09:00
|
|
|
if (e->modifiers() & Qt::ControlModifier) {
|
|
|
|
|
if (!scrollWheelZoomingEnabled()) {
|
|
|
|
|
// When the setting is disabled globally,
|
|
|
|
|
// we have to skip calling QPlainTextEdit::wheelEvent()
|
|
|
|
|
// that changes zoom in it.
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-30 14:37:24 +01:00
|
|
|
const float delta = e->angleDelta().y() / 120.f;
|
|
|
|
|
if (delta != 0)
|
|
|
|
|
zoomF(delta);
|
2008-12-02 12:01:29 +01:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
QPlainTextEdit::wheelEvent(e);
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-01 16:15:48 +01:00
|
|
|
static void showZoomIndicator(QWidget *editor, const int newZoom)
|
|
|
|
|
{
|
|
|
|
|
Utils::FadingIndicator::showText(editor,
|
|
|
|
|
QCoreApplication::translate("TextEditor::TextEditorWidget",
|
|
|
|
|
"Zoom: %1%").arg(newZoom),
|
|
|
|
|
Utils::FadingIndicator::SmallText);
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-30 14:37:24 +01:00
|
|
|
void TextEditorWidget::zoomF(float delta)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2010-05-20 15:10:26 +02:00
|
|
|
d->clearVisibleFoldedBlock();
|
2015-11-30 14:37:24 +01:00
|
|
|
float step = 10.f * delta;
|
|
|
|
|
// Ensure we always zoom a minimal step in-case the resolution is more than 16x
|
|
|
|
|
if (step > 0 && step < 1)
|
|
|
|
|
step = 1;
|
|
|
|
|
else if (step < 0 && step > -1)
|
|
|
|
|
step = -1;
|
|
|
|
|
|
2015-12-18 10:28:00 +02:00
|
|
|
const int newZoom = TextEditorSettings::instance()->increaseFontZoom(int(step));
|
2016-02-01 16:15:48 +01:00
|
|
|
showZoomIndicator(this, newZoom);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::zoomReset()
|
2009-11-30 19:00:36 +01:00
|
|
|
{
|
2015-12-18 10:28:00 +02:00
|
|
|
TextEditorSettings::instance()->resetFontZoom();
|
2016-02-01 16:15:48 +01:00
|
|
|
showZoomIndicator(this, 100);
|
2009-11-30 19:00:36 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
TextEditorWidget::Link TextEditorWidget::findLinkAt(const QTextCursor &, bool, bool)
|
2009-09-08 13:03:24 +02:00
|
|
|
{
|
|
|
|
|
return Link();
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::openLink(const Link &link, bool inNextSplit)
|
2009-09-08 13:03:24 +02:00
|
|
|
{
|
2013-02-06 14:23:18 +01:00
|
|
|
if (!link.hasValidTarget())
|
2009-09-08 13:03:24 +02:00
|
|
|
return false;
|
|
|
|
|
|
2014-12-21 21:54:30 +02:00
|
|
|
if (!inNextSplit && textDocument()->filePath().toString() == link.targetFileName) {
|
2014-08-19 14:23:03 +02:00
|
|
|
EditorManager::addCurrentPositionToNavigationHistory();
|
2017-05-23 14:34:22 +02:00
|
|
|
gotoLine(link.targetLine, link.targetColumn, true, true);
|
2009-09-08 13:03:24 +02:00
|
|
|
setFocus();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2014-09-26 12:03:20 +02:00
|
|
|
EditorManager::OpenEditorFlags flags;
|
|
|
|
|
if (inNextSplit)
|
|
|
|
|
flags |= EditorManager::OpenInOtherSplit;
|
2009-09-08 13:03:24 +02:00
|
|
|
|
2014-09-26 12:03:20 +02:00
|
|
|
return EditorManager::openEditorAt(link.targetFileName, link.targetLine, link.targetColumn,
|
|
|
|
|
Id(), flags);
|
2009-09-08 13:03:24 +02:00
|
|
|
}
|
|
|
|
|
|
2015-09-21 10:27:43 +02:00
|
|
|
void TextEditorWidgetPrivate::requestUpdateLink(QMouseEvent *e, bool immediate)
|
2009-11-03 14:29:49 +01:00
|
|
|
{
|
2015-04-07 09:46:11 +02:00
|
|
|
if (!q->mouseNavigationEnabled())
|
|
|
|
|
return;
|
|
|
|
|
if (e->modifiers() & Qt::ControlModifier) {
|
2009-11-03 14:29:49 +01:00
|
|
|
// Link emulation behaviour for 'go to definition'
|
2014-07-25 16:23:50 +02:00
|
|
|
const QTextCursor cursor = q->cursorForPosition(e->pos());
|
2009-11-03 14:29:49 +01:00
|
|
|
|
2015-09-21 10:27:43 +02:00
|
|
|
// Avoid updating the link we already found
|
|
|
|
|
if (cursor.position() >= m_currentLink.linkTextStart
|
|
|
|
|
&& cursor.position() <= m_currentLink.linkTextEnd)
|
|
|
|
|
return;
|
|
|
|
|
|
2009-11-03 14:29:49 +01:00
|
|
|
// Check that the mouse was actually on the text somewhere
|
2014-07-25 16:23:50 +02:00
|
|
|
bool onText = q->cursorRect(cursor).right() >= e->x();
|
2009-11-03 14:29:49 +01:00
|
|
|
if (!onText) {
|
|
|
|
|
QTextCursor nextPos = cursor;
|
|
|
|
|
nextPos.movePosition(QTextCursor::Right);
|
2014-07-25 16:23:50 +02:00
|
|
|
onText = q->cursorRect(nextPos).right() >= e->x();
|
2009-11-03 14:29:49 +01:00
|
|
|
}
|
|
|
|
|
|
2015-09-21 10:27:43 +02:00
|
|
|
if (onText) {
|
|
|
|
|
m_pendingLinkUpdate = cursor;
|
2009-11-03 14:29:49 +01:00
|
|
|
|
2015-09-21 10:27:43 +02:00
|
|
|
if (immediate)
|
|
|
|
|
updateLink();
|
|
|
|
|
else
|
|
|
|
|
QTimer::singleShot(0, this, &TextEditorWidgetPrivate::updateLink);
|
|
|
|
|
|
|
|
|
|
return;
|
2015-04-07 09:46:11 +02:00
|
|
|
}
|
|
|
|
|
}
|
2015-09-21 10:27:43 +02:00
|
|
|
|
|
|
|
|
clearLink();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TextEditorWidgetPrivate::updateLink()
|
|
|
|
|
{
|
|
|
|
|
if (m_pendingLinkUpdate.isNull())
|
|
|
|
|
return;
|
|
|
|
|
if (m_pendingLinkUpdate == m_lastLinkUpdate)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
m_lastLinkUpdate = m_pendingLinkUpdate;
|
|
|
|
|
const TextEditorWidget::Link link = q->findLinkAt(m_pendingLinkUpdate, false);
|
|
|
|
|
if (link.hasValidLinkText())
|
|
|
|
|
showLink(link);
|
|
|
|
|
else
|
|
|
|
|
clearLink();
|
2009-11-03 14:29:49 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::showLink(const TextEditorWidget::Link &link)
|
2009-09-08 13:03:24 +02:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
if (m_currentLink == link)
|
2009-11-03 14:29:49 +01:00
|
|
|
return;
|
|
|
|
|
|
2009-09-08 13:03:24 +02:00
|
|
|
QTextEdit::ExtraSelection sel;
|
2014-07-25 16:23:50 +02:00
|
|
|
sel.cursor = q->textCursor();
|
2013-02-06 14:23:18 +01:00
|
|
|
sel.cursor.setPosition(link.linkTextStart);
|
|
|
|
|
sel.cursor.setPosition(link.linkTextEnd, QTextCursor::KeepAnchor);
|
2014-08-01 23:31:56 +02:00
|
|
|
sel.format = q->textDocument()->fontSettings().toTextCharFormat(C_LINK);
|
2009-09-08 13:03:24 +02:00
|
|
|
sel.format.setFontUnderline(true);
|
2014-09-26 11:37:54 +02:00
|
|
|
q->setExtraSelections(TextEditorWidget::OtherSelection, QList<QTextEdit::ExtraSelection>() << sel);
|
2014-07-25 16:23:50 +02:00
|
|
|
q->viewport()->setCursor(Qt::PointingHandCursor);
|
|
|
|
|
m_currentLink = link;
|
|
|
|
|
m_linkPressed = false;
|
2009-09-08 13:03:24 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::clearLink()
|
2009-09-08 13:03:24 +02:00
|
|
|
{
|
2015-09-21 10:27:43 +02:00
|
|
|
m_pendingLinkUpdate = QTextCursor();
|
|
|
|
|
m_lastLinkUpdate = QTextCursor();
|
2014-07-25 16:23:50 +02:00
|
|
|
if (!m_currentLink.hasValidLinkText())
|
2009-09-08 13:03:24 +02:00
|
|
|
return;
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
q->setExtraSelections(TextEditorWidget::OtherSelection, QList<QTextEdit::ExtraSelection>());
|
2014-07-25 16:23:50 +02:00
|
|
|
q->viewport()->setCursor(Qt::IBeamCursor);
|
2014-09-26 11:37:54 +02:00
|
|
|
m_currentLink = TextEditorWidget::Link();
|
2014-07-25 16:23:50 +02:00
|
|
|
m_linkPressed = false;
|
2009-09-08 13:03:24 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::highlightSearchResultsSlot(const QString &txt, FindFlags findFlags)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-08-01 15:06:28 +02:00
|
|
|
if (m_searchExpr.pattern() == txt)
|
2008-12-02 12:01:29 +01:00
|
|
|
return;
|
2014-08-01 15:06:28 +02:00
|
|
|
m_searchExpr.setPattern(txt);
|
|
|
|
|
m_searchExpr.setPatternSyntax((findFlags & FindRegularExpression) ?
|
2009-05-29 16:52:12 +02:00
|
|
|
QRegExp::RegExp : QRegExp::FixedString);
|
2014-08-01 15:06:28 +02:00
|
|
|
m_searchExpr.setCaseSensitivity((findFlags & FindCaseSensitively) ?
|
2008-12-02 12:01:29 +01:00
|
|
|
Qt::CaseSensitive : Qt::CaseInsensitive);
|
2014-08-01 15:06:28 +02:00
|
|
|
m_findFlags = findFlags;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2014-08-01 15:06:28 +02:00
|
|
|
m_delayedUpdateTimer.start(50);
|
2015-06-04 10:43:34 +02:00
|
|
|
|
|
|
|
|
if (m_highlightScrollBar)
|
|
|
|
|
m_scrollBarUpdateTimer.start(50);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TextEditorWidgetPrivate::searchResultsReady(int beginIndex, int endIndex)
|
|
|
|
|
{
|
|
|
|
|
QVector<SearchResult> results;
|
|
|
|
|
for (int index = beginIndex; index < endIndex; ++index) {
|
|
|
|
|
foreach (Utils::FileSearchResult result, m_searchWatcher->resultAt(index)) {
|
|
|
|
|
const QTextBlock &block = q->document()->findBlockByNumber(result.lineNumber - 1);
|
|
|
|
|
const int matchStart = block.position() + result.matchStart;
|
|
|
|
|
if (!q->inFindScope(matchStart, matchStart + result.matchLength))
|
|
|
|
|
continue;
|
|
|
|
|
results << SearchResult{matchStart, result.matchLength};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
m_searchResults << results;
|
|
|
|
|
addSearchResultsToScrollBar(results);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TextEditorWidgetPrivate::searchFinished()
|
|
|
|
|
{
|
|
|
|
|
delete m_searchWatcher;
|
|
|
|
|
m_searchWatcher = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TextEditorWidgetPrivate::adjustScrollBarRanges()
|
|
|
|
|
{
|
|
|
|
|
if (!m_highlightScrollBar)
|
|
|
|
|
return;
|
|
|
|
|
const float lineSpacing = QFontMetricsF(q->font()).lineSpacing();
|
|
|
|
|
if (lineSpacing == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const float offset = q->contentOffset().y();
|
|
|
|
|
m_highlightScrollBar->setVisibleRange((q->viewport()->rect().height() - offset) / lineSpacing);
|
|
|
|
|
m_highlightScrollBar->setRangeOffset(offset / lineSpacing);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TextEditorWidgetPrivate::highlightSearchResultsInScrollBar()
|
|
|
|
|
{
|
|
|
|
|
if (!m_highlightScrollBar)
|
|
|
|
|
return;
|
|
|
|
|
m_highlightScrollBar->removeHighlights(Constants::SCROLL_BAR_SEARCH_RESULT);
|
|
|
|
|
m_searchResults.clear();
|
|
|
|
|
|
|
|
|
|
if (m_searchWatcher) {
|
|
|
|
|
m_searchWatcher->disconnect();
|
|
|
|
|
m_searchWatcher->cancel();
|
|
|
|
|
m_searchWatcher->deleteLater();
|
|
|
|
|
m_searchWatcher = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QString &txt = m_searchExpr.pattern();
|
|
|
|
|
if (txt.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
adjustScrollBarRanges();
|
|
|
|
|
|
|
|
|
|
m_searchWatcher = new QFutureWatcher<FileSearchResultList>();
|
|
|
|
|
connect(m_searchWatcher, &QFutureWatcher<Utils::FileSearchResultList>::resultsReadyAt,
|
|
|
|
|
this, &TextEditorWidgetPrivate::searchResultsReady);
|
|
|
|
|
connect(m_searchWatcher, &QFutureWatcher<Utils::FileSearchResultList>::finished,
|
|
|
|
|
this, &TextEditorWidgetPrivate::searchFinished);
|
|
|
|
|
m_searchWatcher->setPendingResultsLimit(10);
|
|
|
|
|
|
|
|
|
|
const QTextDocument::FindFlags findFlags = textDocumentFlagsForFindFlags(m_findFlags);
|
|
|
|
|
|
|
|
|
|
const QString &fileName = m_document->filePath().toString();
|
|
|
|
|
FileListIterator *it =
|
2017-02-22 15:09:35 +01:00
|
|
|
new FileListIterator({fileName} , {const_cast<QTextCodec *>(m_document->codec())});
|
2015-06-04 10:43:34 +02:00
|
|
|
QMap<QString, QString> fileToContentsMap;
|
|
|
|
|
fileToContentsMap[fileName] = m_document->plainText();
|
|
|
|
|
|
|
|
|
|
if (m_findFlags & FindRegularExpression)
|
|
|
|
|
m_searchWatcher->setFuture(findInFilesRegExp(txt, it, findFlags, fileToContentsMap));
|
|
|
|
|
else
|
|
|
|
|
m_searchWatcher->setFuture(findInFiles(txt, it, findFlags, fileToContentsMap));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TextEditorWidgetPrivate::scheduleUpdateHighlightScrollBar()
|
|
|
|
|
{
|
|
|
|
|
if (m_scrollBarUpdateScheduled)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
m_scrollBarUpdateScheduled = true;
|
|
|
|
|
QTimer::singleShot(0, this, &TextEditorWidgetPrivate::updateHighlightScrollBarNow);
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-01 12:06:03 +02:00
|
|
|
Highlight::Priority textMarkPrioToScrollBarPrio(const TextMark::Priority &prio)
|
2015-06-04 10:43:34 +02:00
|
|
|
{
|
|
|
|
|
switch (prio) {
|
|
|
|
|
case TextMark::LowPriority:
|
2017-06-01 12:06:03 +02:00
|
|
|
return Highlight::LowPriority;
|
2015-06-04 10:43:34 +02:00
|
|
|
case TextMark::NormalPriority:
|
2017-06-01 12:06:03 +02:00
|
|
|
return Highlight::NormalPriority;
|
2015-06-04 10:43:34 +02:00
|
|
|
case TextMark::HighPriority:
|
2017-06-01 12:06:03 +02:00
|
|
|
return Highlight::HighPriority;
|
2015-06-04 10:43:34 +02:00
|
|
|
default:
|
2017-06-01 12:06:03 +02:00
|
|
|
return Highlight::NormalPriority;
|
2015-06-04 10:43:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TextEditorWidgetPrivate::addSearchResultsToScrollBar(QVector<SearchResult> results)
|
|
|
|
|
{
|
2017-06-01 12:06:03 +02:00
|
|
|
if (!m_highlightScrollBar)
|
|
|
|
|
return;
|
2015-06-04 10:43:34 +02:00
|
|
|
foreach (SearchResult result, results) {
|
|
|
|
|
const QTextBlock &block = q->document()->findBlock(result.start);
|
|
|
|
|
if (block.isValid() && block.isVisible()) {
|
|
|
|
|
const int firstLine = block.layout()->lineForTextPosition(result.start - block.position()).lineNumber();
|
|
|
|
|
const int lastLine = block.layout()->lineForTextPosition(result.start - block.position() + result.length).lineNumber();
|
2017-06-01 12:06:03 +02:00
|
|
|
for (int line = firstLine; line <= lastLine; ++line) {
|
|
|
|
|
m_highlightScrollBar->addHighlight(
|
|
|
|
|
{Constants::SCROLL_BAR_SEARCH_RESULT, block.firstLineNumber() + line,
|
|
|
|
|
Theme::TextEditor_SearchResult_ScrollBarColor, Highlight::HighPriority});
|
|
|
|
|
}
|
2015-06-04 10:43:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-01 12:06:03 +02:00
|
|
|
Highlight markToHighlight(TextMark *mark, int lineNumber)
|
2015-06-04 10:43:34 +02:00
|
|
|
{
|
2017-06-01 08:48:11 +02:00
|
|
|
return Highlight(mark->category(), lineNumber, mark->color(),
|
2017-06-01 12:06:03 +02:00
|
|
|
textMarkPrioToScrollBarPrio(mark->priority()));
|
|
|
|
|
}
|
2015-06-04 10:43:34 +02:00
|
|
|
|
2017-06-01 12:06:03 +02:00
|
|
|
void TextEditorWidgetPrivate::updateHighlightScrollBarNow()
|
|
|
|
|
{
|
2015-06-04 10:43:34 +02:00
|
|
|
m_scrollBarUpdateScheduled = false;
|
|
|
|
|
if (!m_highlightScrollBar)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
m_highlightScrollBar->removeAllHighlights();
|
|
|
|
|
|
|
|
|
|
updateCurrentLineInScrollbar();
|
|
|
|
|
|
|
|
|
|
// update search results
|
|
|
|
|
addSearchResultsToScrollBar(m_searchResults);
|
|
|
|
|
|
|
|
|
|
// update text marks
|
|
|
|
|
foreach (TextMark *mark, m_document->marks()) {
|
2017-06-01 08:48:11 +02:00
|
|
|
if (!mark->isVisible() || !mark->hasColor())
|
2015-06-04 10:43:34 +02:00
|
|
|
continue;
|
|
|
|
|
const QTextBlock &block = q->document()->findBlockByNumber(mark->lineNumber() - 1);
|
|
|
|
|
if (block.isVisible())
|
2017-06-01 12:06:03 +02:00
|
|
|
m_highlightScrollBar->addHighlight(markToHighlight(mark, block.firstLineNumber()));
|
2015-06-04 10:43:34 +02:00
|
|
|
}
|
2009-12-01 14:47:10 +01:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
int TextEditorWidget::verticalBlockSelectionFirstColumn() const
|
2010-04-12 20:49:40 +02:00
|
|
|
{
|
2014-06-16 14:20:36 +02:00
|
|
|
return d->m_inBlockSelectionMode ? d->m_blockSelection.firstVisualColumn() : -1;
|
2010-08-05 15:01:20 +02:00
|
|
|
}
|
2010-04-12 20:49:40 +02:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
int TextEditorWidget::verticalBlockSelectionLastColumn() const
|
2010-08-05 15:01:20 +02:00
|
|
|
{
|
2014-06-16 14:20:36 +02:00
|
|
|
return d->m_inBlockSelectionMode ? d->m_blockSelection.lastVisualColumn() : -1;
|
2010-04-12 20:49:40 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
QRegion TextEditorWidget::translatedLineRegion(int lineStart, int lineEnd) const
|
2010-07-09 16:09:08 +02:00
|
|
|
{
|
|
|
|
|
QRegion region;
|
|
|
|
|
for (int i = lineStart ; i <= lineEnd; i++) {
|
|
|
|
|
QTextBlock block = document()->findBlockByNumber(i);
|
|
|
|
|
QPoint topLeft = blockBoundingGeometry(block).translated(contentOffset()).topLeft().toPoint();
|
|
|
|
|
|
2010-07-14 10:41:57 +02:00
|
|
|
if (block.isValid()) {
|
|
|
|
|
QTextLayout *layout = block.layout();
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < layout->lineCount();i++) {
|
|
|
|
|
QTextLine line = layout->lineAt(i);
|
|
|
|
|
region += line.naturalTextRect().translated(topLeft).toRect();
|
|
|
|
|
}
|
2010-07-09 16:09:08 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return region;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::setFindScope(const QTextCursor &start, const QTextCursor &end,
|
2010-08-05 15:01:20 +02:00
|
|
|
int verticalBlockSelectionFirstColumn,
|
|
|
|
|
int verticalBlockSelectionLastColumn)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-08-01 15:06:28 +02:00
|
|
|
if (start != m_findScopeStart
|
|
|
|
|
|| end != m_findScopeEnd
|
|
|
|
|
|| verticalBlockSelectionFirstColumn != m_findScopeVerticalBlockSelectionFirstColumn
|
|
|
|
|
|| verticalBlockSelectionLastColumn != m_findScopeVerticalBlockSelectionLastColumn) {
|
|
|
|
|
m_findScopeStart = start;
|
|
|
|
|
m_findScopeEnd = end;
|
|
|
|
|
m_findScopeVerticalBlockSelectionFirstColumn = verticalBlockSelectionFirstColumn;
|
|
|
|
|
m_findScopeVerticalBlockSelectionLastColumn = verticalBlockSelectionLastColumn;
|
|
|
|
|
q->viewport()->update();
|
2015-06-04 10:43:34 +02:00
|
|
|
highlightSearchResultsInScrollBar();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-05-03 10:59:30 +02:00
|
|
|
void TextEditorWidgetPrivate::_q_animateUpdate(const QTextCursor &cursor,
|
|
|
|
|
QPointF lastPos, QRectF rect)
|
2009-04-28 18:34:58 +02:00
|
|
|
{
|
2014-08-01 15:06:28 +02:00
|
|
|
q->viewport()->update(QRectF(q->cursorRect(cursor).topLeft() + rect.topLeft(), rect.size()).toAlignedRect());
|
2009-09-23 12:50:02 +02:00
|
|
|
if (!lastPos.isNull())
|
2014-08-01 15:06:28 +02:00
|
|
|
q->viewport()->update(QRectF(lastPos + rect.topLeft(), rect.size()).toAlignedRect());
|
2009-04-28 18:34:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
TextEditorAnimator::TextEditorAnimator(QObject *parent)
|
2014-08-20 22:01:53 +02:00
|
|
|
: QObject(parent), m_timeline(256)
|
2009-04-28 18:34:58 +02:00
|
|
|
{
|
|
|
|
|
m_value = 0;
|
2014-08-20 22:01:53 +02:00
|
|
|
m_timeline.setCurveShape(QTimeLine::SineCurve);
|
2014-09-26 11:37:54 +02:00
|
|
|
connect(&m_timeline, &QTimeLine::valueChanged, this, &TextEditorAnimator::step);
|
2014-08-20 22:01:53 +02:00
|
|
|
connect(&m_timeline, &QTimeLine::finished, this, &QObject::deleteLater);
|
|
|
|
|
m_timeline.start();
|
2009-04-28 18:34:58 +02:00
|
|
|
}
|
|
|
|
|
|
2016-05-03 10:59:30 +02:00
|
|
|
void TextEditorAnimator::init(const QTextCursor &cursor, const QFont &f, const QPalette &pal)
|
2009-04-28 18:34:58 +02:00
|
|
|
{
|
2016-05-03 10:59:30 +02:00
|
|
|
m_cursor = cursor;
|
2009-05-04 11:10:22 +02:00
|
|
|
m_font = f;
|
|
|
|
|
m_palette = pal;
|
2016-05-03 10:59:30 +02:00
|
|
|
m_text = cursor.selectedText();
|
2009-05-04 11:10:22 +02:00
|
|
|
QFontMetrics fm(m_font);
|
|
|
|
|
m_size = QSizeF(fm.width(m_text), fm.height());
|
|
|
|
|
}
|
2009-04-28 18:34:58 +02:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorAnimator::draw(QPainter *p, const QPointF &pos)
|
2009-04-28 18:34:58 +02:00
|
|
|
{
|
2009-09-23 12:50:02 +02:00
|
|
|
m_lastDrawPos = pos;
|
2009-04-28 18:34:58 +02:00
|
|
|
p->setPen(m_palette.text().color());
|
|
|
|
|
QFont f = m_font;
|
2009-04-29 12:17:42 +02:00
|
|
|
f.setPointSizeF(f.pointSizeF() * (1.0 + m_value/2));
|
2009-04-28 18:34:58 +02:00
|
|
|
QFontMetrics fm(f);
|
|
|
|
|
int width = fm.width(m_text);
|
|
|
|
|
QRectF r((m_size.width()-width)/2, (m_size.height() - fm.height())/2, width, fm.height());
|
|
|
|
|
r.translate(pos);
|
|
|
|
|
p->fillRect(r, m_palette.base());
|
|
|
|
|
p->setFont(f);
|
|
|
|
|
p->drawText(r, m_text);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorAnimator::isRunning() const
|
2009-04-28 19:02:58 +02:00
|
|
|
{
|
2014-08-20 22:01:53 +02:00
|
|
|
return m_timeline.state() == QTimeLine::Running;
|
2009-04-28 19:02:58 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
QRectF TextEditorAnimator::rect() const
|
2009-04-28 18:34:58 +02:00
|
|
|
{
|
|
|
|
|
QFont f = m_font;
|
2009-04-29 12:17:42 +02:00
|
|
|
f.setPointSizeF(f.pointSizeF() * (1.0 + m_value/2));
|
2009-04-28 18:34:58 +02:00
|
|
|
QFontMetrics fm(f);
|
|
|
|
|
int width = fm.width(m_text);
|
|
|
|
|
return QRectF((m_size.width()-width)/2, (m_size.height() - fm.height())/2, width, fm.height());
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorAnimator::step(qreal v)
|
2009-04-28 18:34:58 +02:00
|
|
|
{
|
|
|
|
|
QRectF before = rect();
|
|
|
|
|
m_value = v;
|
|
|
|
|
QRectF after = rect();
|
2016-05-03 10:59:30 +02:00
|
|
|
emit updateRequest(m_cursor, m_lastDrawPos, before.united(after));
|
2009-04-28 18:34:58 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorAnimator::finish()
|
2009-04-28 18:34:58 +02:00
|
|
|
{
|
2014-08-20 22:01:53 +02:00
|
|
|
m_timeline.stop();
|
2009-04-28 18:34:58 +02:00
|
|
|
step(0);
|
|
|
|
|
deleteLater();
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::_q_matchParentheses()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-08-01 15:06:28 +02:00
|
|
|
if (q->isReadOnly()
|
|
|
|
|
|| !(m_displaySettings.m_highlightMatchingParentheses
|
|
|
|
|
|| m_displaySettings.m_animateMatchingParentheses))
|
2008-12-02 12:01:29 +01:00
|
|
|
return;
|
|
|
|
|
|
2014-08-01 15:06:28 +02:00
|
|
|
QTextCursor backwardMatch = q->textCursor();
|
|
|
|
|
QTextCursor forwardMatch = q->textCursor();
|
|
|
|
|
if (q->overwriteMode())
|
2012-09-24 19:29:26 +02:00
|
|
|
backwardMatch.movePosition(QTextCursor::Right);
|
2008-12-02 12:01:29 +01:00
|
|
|
const TextBlockUserData::MatchType backwardMatchType = TextBlockUserData::matchCursorBackward(&backwardMatch);
|
|
|
|
|
const TextBlockUserData::MatchType forwardMatchType = TextBlockUserData::matchCursorForward(&forwardMatch);
|
|
|
|
|
|
2008-12-05 13:19:57 +01:00
|
|
|
QList<QTextEdit::ExtraSelection> extraSelections;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2009-04-27 13:01:28 +02:00
|
|
|
if (backwardMatchType == TextBlockUserData::NoMatch && forwardMatchType == TextBlockUserData::NoMatch) {
|
2014-09-26 11:37:54 +02:00
|
|
|
q->setExtraSelections(TextEditorWidget::ParenthesesMatchingSelection, extraSelections); // clear
|
2009-04-27 13:01:28 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-21 14:29:25 +01:00
|
|
|
const QTextCharFormat &matchFormat
|
2014-08-01 23:31:56 +02:00
|
|
|
= q->textDocument()->fontSettings().toTextCharFormat(C_PARENTHESES);
|
2015-04-28 11:04:07 +02:00
|
|
|
const QTextCharFormat &mismatchFormat
|
|
|
|
|
= q->textDocument()->fontSettings().toTextCharFormat(C_PARENTHESES_MISMATCH);
|
2009-04-28 18:34:58 +02:00
|
|
|
int animatePosition = -1;
|
2008-12-02 12:01:29 +01:00
|
|
|
if (backwardMatch.hasSelection()) {
|
|
|
|
|
QTextEdit::ExtraSelection sel;
|
|
|
|
|
if (backwardMatchType == TextBlockUserData::Mismatch) {
|
|
|
|
|
sel.cursor = backwardMatch;
|
2015-04-28 11:04:07 +02:00
|
|
|
sel.format = mismatchFormat;
|
2012-09-13 15:23:45 +02:00
|
|
|
extraSelections.append(sel);
|
2008-12-02 12:01:29 +01:00
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
sel.cursor = backwardMatch;
|
2014-01-21 14:29:25 +01:00
|
|
|
sel.format = matchFormat;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
sel.cursor.setPosition(backwardMatch.selectionStart());
|
2012-11-12 13:46:17 +01:00
|
|
|
sel.cursor.setPosition(sel.cursor.position() + 1, QTextCursor::KeepAnchor);
|
2008-12-02 12:01:29 +01:00
|
|
|
extraSelections.append(sel);
|
|
|
|
|
|
2014-08-01 15:06:28 +02:00
|
|
|
if (m_displaySettings.m_animateMatchingParentheses && sel.cursor.block().isVisible())
|
2012-11-12 13:03:10 +01:00
|
|
|
animatePosition = backwardMatch.selectionStart();
|
|
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
sel.cursor.setPosition(backwardMatch.selectionEnd());
|
2012-11-12 13:46:17 +01:00
|
|
|
sel.cursor.setPosition(sel.cursor.position() - 1, QTextCursor::KeepAnchor);
|
2012-09-13 15:23:45 +02:00
|
|
|
extraSelections.append(sel);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (forwardMatch.hasSelection()) {
|
|
|
|
|
QTextEdit::ExtraSelection sel;
|
|
|
|
|
if (forwardMatchType == TextBlockUserData::Mismatch) {
|
|
|
|
|
sel.cursor = forwardMatch;
|
2015-04-28 11:04:07 +02:00
|
|
|
sel.format = mismatchFormat;
|
2012-09-13 15:23:45 +02:00
|
|
|
extraSelections.append(sel);
|
2008-12-02 12:01:29 +01:00
|
|
|
} else {
|
|
|
|
|
|
|
|
|
|
sel.cursor = forwardMatch;
|
2014-01-21 14:29:25 +01:00
|
|
|
sel.format = matchFormat;
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
sel.cursor.setPosition(forwardMatch.selectionStart());
|
2012-11-12 13:46:17 +01:00
|
|
|
sel.cursor.setPosition(sel.cursor.position() + 1, QTextCursor::KeepAnchor);
|
2008-12-02 12:01:29 +01:00
|
|
|
extraSelections.append(sel);
|
|
|
|
|
|
|
|
|
|
sel.cursor.setPosition(forwardMatch.selectionEnd());
|
2012-11-12 13:46:17 +01:00
|
|
|
sel.cursor.setPosition(sel.cursor.position() - 1, QTextCursor::KeepAnchor);
|
|
|
|
|
extraSelections.append(sel);
|
|
|
|
|
|
2014-08-01 15:06:28 +02:00
|
|
|
if (m_displaySettings.m_animateMatchingParentheses && sel.cursor.block().isVisible())
|
2012-11-12 13:03:10 +01:00
|
|
|
animatePosition = forwardMatch.selectionEnd() - 1;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
}
|
2009-04-28 18:34:58 +02:00
|
|
|
|
|
|
|
|
|
2009-05-06 11:18:56 +02:00
|
|
|
if (animatePosition >= 0) {
|
2014-09-26 11:37:54 +02:00
|
|
|
foreach (const QTextEdit::ExtraSelection &sel, q->extraSelections(TextEditorWidget::ParenthesesMatchingSelection)) {
|
2009-05-06 11:18:56 +02:00
|
|
|
if (sel.cursor.selectionStart() == animatePosition
|
|
|
|
|
|| sel.cursor.selectionEnd() - 1 == animatePosition) {
|
|
|
|
|
animatePosition = -1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-04-28 18:34:58 +02:00
|
|
|
if (animatePosition >= 0) {
|
2016-04-28 12:51:34 +02:00
|
|
|
cancelCurrentAnimations();// one animation is enough
|
2009-04-28 18:34:58 +02:00
|
|
|
QPalette pal;
|
2014-01-21 14:29:25 +01:00
|
|
|
pal.setBrush(QPalette::Text, matchFormat.foreground());
|
|
|
|
|
pal.setBrush(QPalette::Base, matchFormat.background());
|
2016-05-03 10:59:30 +02:00
|
|
|
QTextCursor cursor = q->textCursor();
|
|
|
|
|
cursor.setPosition(animatePosition + 1);
|
|
|
|
|
cursor.setPosition(animatePosition, QTextCursor::KeepAnchor);
|
|
|
|
|
m_bracketsAnimator = new TextEditorAnimator(this);
|
|
|
|
|
m_bracketsAnimator->init(cursor, q->font(), pal);
|
2016-04-28 12:51:34 +02:00
|
|
|
connect(m_bracketsAnimator.data(), &TextEditorAnimator::updateRequest,
|
2014-09-26 11:37:54 +02:00
|
|
|
this, &TextEditorWidgetPrivate::_q_animateUpdate);
|
2009-05-06 11:18:56 +02:00
|
|
|
}
|
2014-08-01 15:06:28 +02:00
|
|
|
if (m_displaySettings.m_highlightMatchingParentheses)
|
2014-09-26 11:37:54 +02:00
|
|
|
q->setExtraSelections(TextEditorWidget::ParenthesesMatchingSelection, extraSelections);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::_q_highlightBlocks()
|
2009-04-23 17:28:53 +02:00
|
|
|
{
|
2014-09-26 11:37:54 +02:00
|
|
|
TextEditorPrivateHighlightBlocks highlightBlocksInfo;
|
2009-04-24 16:44:48 +02:00
|
|
|
|
2010-05-20 15:10:26 +02:00
|
|
|
QTextBlock block;
|
2014-08-01 15:06:28 +02:00
|
|
|
if (extraAreaHighlightFoldedBlockNumber >= 0) {
|
|
|
|
|
block = q->document()->findBlockByNumber(extraAreaHighlightFoldedBlockNumber);
|
2010-05-20 15:10:26 +02:00
|
|
|
if (block.isValid()
|
|
|
|
|
&& block.next().isValid()
|
2014-09-26 09:14:03 +02:00
|
|
|
&& TextDocumentLayout::foldingIndent(block.next())
|
|
|
|
|
> TextDocumentLayout::foldingIndent(block))
|
2010-05-20 15:10:26 +02:00
|
|
|
block = block.next();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QTextBlock closeBlock = block;
|
|
|
|
|
while (block.isValid()) {
|
2014-09-26 09:14:03 +02:00
|
|
|
int foldingIndent = TextDocumentLayout::foldingIndent(block);
|
2010-05-20 15:10:26 +02:00
|
|
|
|
2014-09-26 09:14:03 +02:00
|
|
|
while (block.previous().isValid() && TextDocumentLayout::foldingIndent(block) >= foldingIndent)
|
2010-05-20 15:10:26 +02:00
|
|
|
block = block.previous();
|
2014-09-26 09:14:03 +02:00
|
|
|
int nextIndent = TextDocumentLayout::foldingIndent(block);
|
2010-05-20 15:10:26 +02:00
|
|
|
if (nextIndent == foldingIndent)
|
|
|
|
|
break;
|
|
|
|
|
highlightBlocksInfo.open.prepend(block.blockNumber());
|
|
|
|
|
while (closeBlock.next().isValid()
|
2014-09-26 09:14:03 +02:00
|
|
|
&& TextDocumentLayout::foldingIndent(closeBlock.next()) >= foldingIndent )
|
2010-05-20 15:10:26 +02:00
|
|
|
closeBlock = closeBlock.next();
|
|
|
|
|
highlightBlocksInfo.close.append(closeBlock.blockNumber());
|
2014-08-01 15:06:28 +02:00
|
|
|
int indent = qMin(visualIndent(block), visualIndent(closeBlock));
|
|
|
|
|
highlightBlocksInfo.visualIndent.prepend(indent);
|
2010-05-20 15:10:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
|
if (block.isValid()) {
|
|
|
|
|
QTextCursor cursor(block);
|
2014-08-01 15:06:28 +02:00
|
|
|
if (extraAreaHighlightCollapseColumn >= 0)
|
|
|
|
|
cursor.setPosition(cursor.position() + qMin(extraAreaHighlightCollapseColumn,
|
2010-05-20 15:10:26 +02:00
|
|
|
block.length()-1));
|
|
|
|
|
QTextCursor closeCursor;
|
|
|
|
|
bool firstRun = true;
|
|
|
|
|
while (TextBlockUserData::findPreviousBlockOpenParenthesis(&cursor, firstRun)) {
|
|
|
|
|
firstRun = false;
|
|
|
|
|
highlightBlocksInfo.open.prepend(cursor.blockNumber());
|
2014-08-01 15:06:28 +02:00
|
|
|
int visualIndent = visualIndent(cursor.block());
|
2010-05-20 15:10:26 +02:00
|
|
|
if (closeCursor.isNull())
|
|
|
|
|
closeCursor = cursor;
|
|
|
|
|
if (TextBlockUserData::findNextBlockClosingParenthesis(&closeCursor)) {
|
|
|
|
|
highlightBlocksInfo.close.append(closeCursor.blockNumber());
|
2014-08-01 15:06:28 +02:00
|
|
|
visualIndent = qMin(visualIndent, visualIndent(closeCursor.block()));
|
2009-04-24 16:44:48 +02:00
|
|
|
}
|
2010-05-20 15:10:26 +02:00
|
|
|
highlightBlocksInfo.visualIndent.prepend(visualIndent);
|
2009-04-24 16:44:48 +02:00
|
|
|
}
|
2009-04-23 17:28:53 +02:00
|
|
|
}
|
2010-05-20 15:10:26 +02:00
|
|
|
#endif
|
2014-08-01 15:06:28 +02:00
|
|
|
if (m_highlightBlocksInfo != highlightBlocksInfo) {
|
|
|
|
|
m_highlightBlocksInfo = highlightBlocksInfo;
|
|
|
|
|
q->viewport()->update();
|
|
|
|
|
m_extraArea->update();
|
2009-04-23 17:28:53 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-01 10:19:59 +02:00
|
|
|
void TextEditorWidgetPrivate::autocompleterHighlight(const QTextCursor &cursor)
|
2016-04-28 12:51:34 +02:00
|
|
|
{
|
2016-06-01 10:19:59 +02:00
|
|
|
if ((!m_animateAutoComplete && !m_highlightAutoComplete)
|
|
|
|
|
|| q->isReadOnly() || !cursor.hasSelection()) {
|
2017-06-26 08:17:29 +02:00
|
|
|
m_autoCompleteHighlightPos.clear();
|
|
|
|
|
} else if (m_highlightAutoComplete) {
|
|
|
|
|
m_autoCompleteHighlightPos.push_back(cursor);
|
2017-07-03 14:25:47 +02:00
|
|
|
}
|
|
|
|
|
if (m_animateAutoComplete) {
|
2017-06-26 08:17:29 +02:00
|
|
|
const QTextCharFormat &matchFormat
|
|
|
|
|
= q->textDocument()->fontSettings().toTextCharFormat(C_AUTOCOMPLETE);
|
2016-06-01 10:19:59 +02:00
|
|
|
cancelCurrentAnimations();// one animation is enough
|
|
|
|
|
QPalette pal;
|
|
|
|
|
pal.setBrush(QPalette::Text, matchFormat.foreground());
|
|
|
|
|
pal.setBrush(QPalette::Base, matchFormat.background());
|
2016-05-03 10:59:30 +02:00
|
|
|
m_autocompleteAnimator = new TextEditorAnimator(this);
|
|
|
|
|
m_autocompleteAnimator->init(cursor, q->font(), pal);
|
2016-06-01 10:19:59 +02:00
|
|
|
connect(m_autocompleteAnimator.data(), &TextEditorAnimator::updateRequest,
|
|
|
|
|
this, &TextEditorWidgetPrivate::_q_animateUpdate);
|
|
|
|
|
}
|
2017-06-26 08:17:29 +02:00
|
|
|
updateAutoCompleteHighlight();
|
2016-04-28 12:51:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TextEditorWidgetPrivate::updateAnimator(QPointer<TextEditorAnimator> animator,
|
|
|
|
|
QPainter &painter)
|
|
|
|
|
{
|
2016-05-03 10:59:30 +02:00
|
|
|
if (animator && animator->isRunning())
|
|
|
|
|
animator->draw(&painter, q->cursorRect(animator->cursor()).topLeft());
|
2016-04-28 12:51:34 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TextEditorWidgetPrivate::cancelCurrentAnimations()
|
|
|
|
|
{
|
|
|
|
|
if (m_autocompleteAnimator)
|
|
|
|
|
m_autocompleteAnimator->finish();
|
|
|
|
|
if (m_bracketsAnimator)
|
|
|
|
|
m_bracketsAnimator->finish();
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::changeEvent(QEvent *e)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
QPlainTextEdit::changeEvent(e);
|
|
|
|
|
if (e->type() == QEvent::ApplicationFontChange
|
|
|
|
|
|| e->type() == QEvent::FontChange) {
|
|
|
|
|
if (d->m_extraArea) {
|
|
|
|
|
QFont f = d->m_extraArea->font();
|
2010-10-15 17:19:36 +02:00
|
|
|
f.setPointSizeF(font().pointSizeF());
|
2008-12-02 12:01:29 +01:00
|
|
|
d->m_extraArea->setFont(f);
|
2014-08-01 15:06:28 +02:00
|
|
|
d->slotUpdateExtraAreaWidth();
|
2008-12-02 12:01:29 +01:00
|
|
|
d->m_extraArea->update();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::focusInEvent(QFocusEvent *e)
|
2009-12-01 19:44:31 +01:00
|
|
|
{
|
|
|
|
|
QPlainTextEdit::focusInEvent(e);
|
2014-07-25 16:23:50 +02:00
|
|
|
d->updateHighlights();
|
2009-12-01 19:44:31 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::focusOutEvent(QFocusEvent *e)
|
2009-11-30 19:13:20 +01:00
|
|
|
{
|
|
|
|
|
QPlainTextEdit::focusOutEvent(e);
|
|
|
|
|
if (viewport()->cursor().shape() == Qt::BlankCursor)
|
|
|
|
|
viewport()->setCursor(Qt::IBeamCursor);
|
2016-06-01 10:19:59 +02:00
|
|
|
d->updateHighlights();
|
2009-11-30 19:13:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::maybeSelectLine()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
QTextCursor cursor = q->textCursor();
|
2008-12-02 12:01:29 +01:00
|
|
|
if (!cursor.hasSelection()) {
|
|
|
|
|
const QTextBlock &block = cursor.block();
|
|
|
|
|
if (block.next().isValid()) {
|
|
|
|
|
cursor.setPosition(block.position());
|
|
|
|
|
cursor.setPosition(block.next().position(), QTextCursor::KeepAnchor);
|
|
|
|
|
} else {
|
|
|
|
|
cursor.movePosition(QTextCursor::EndOfBlock);
|
|
|
|
|
cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
|
|
|
|
|
cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
|
|
|
|
|
}
|
2014-07-25 16:23:50 +02:00
|
|
|
q->setTextCursor(cursor);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2009-05-14 17:24:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// shift+del
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::cutLine()
|
2009-05-14 17:24:35 +02:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
d->maybeSelectLine();
|
2008-12-02 12:01:29 +01:00
|
|
|
cut();
|
|
|
|
|
}
|
|
|
|
|
|
2011-03-17 11:07:24 +01:00
|
|
|
// ctrl+ins
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::copyLine()
|
2010-11-27 14:02:20 +09:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
d->maybeSelectLine();
|
2010-11-27 14:02:20 +09:00
|
|
|
copy();
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-26 15:55:04 +02:00
|
|
|
void TextEditorWidgetPrivate::duplicateSelection(bool comment)
|
|
|
|
|
{
|
|
|
|
|
if (m_inBlockSelectionMode) {
|
|
|
|
|
duplicateBlockSelection(comment);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QTextCursor cursor = q->textCursor();
|
|
|
|
|
|
|
|
|
|
if (cursor.hasSelection()) {
|
|
|
|
|
|
|
|
|
|
// Cannot "duplicate and comment" files without multi-line comment
|
|
|
|
|
if (comment && !m_commentDefinition.hasMultiLineStyle())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
QString dupText = cursor.selectedText().replace(QChar::ParagraphSeparator, QLatin1Char('\n'));
|
|
|
|
|
if (comment)
|
|
|
|
|
dupText = (m_commentDefinition.multiLineStart + dupText + m_commentDefinition.multiLineEnd);
|
|
|
|
|
const int selStart = cursor.selectionStart();
|
|
|
|
|
const int selEnd = cursor.selectionEnd();
|
|
|
|
|
const bool cursorAtStart = (cursor.position() == selStart);
|
|
|
|
|
cursor.setPosition(selEnd);
|
|
|
|
|
cursor.insertText(dupText);
|
|
|
|
|
cursor.setPosition(cursorAtStart ? selEnd : selStart);
|
|
|
|
|
cursor.setPosition(cursorAtStart ? selStart : selEnd, QTextCursor::KeepAnchor);
|
|
|
|
|
} else {
|
|
|
|
|
const int curPos = cursor.position();
|
|
|
|
|
const QTextBlock &block = cursor.block();
|
|
|
|
|
QString dupText = block.text() + QLatin1Char('\n');
|
|
|
|
|
if (comment && m_commentDefinition.hasSingleLineStyle())
|
|
|
|
|
dupText.append(m_commentDefinition.singleLine);
|
|
|
|
|
cursor.setPosition(block.position());
|
|
|
|
|
cursor.insertText(dupText);
|
|
|
|
|
cursor.setPosition(curPos);
|
|
|
|
|
}
|
|
|
|
|
q->setTextCursor(cursor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TextEditorWidgetPrivate::duplicateBlockSelection(bool comment)
|
|
|
|
|
{
|
|
|
|
|
QTextCursor cursor = q->textCursor();
|
|
|
|
|
|
|
|
|
|
const TextBlockSelection curSel = m_blockSelection;
|
|
|
|
|
|
|
|
|
|
if (curSel.positionColumn == curSel.anchorColumn) {
|
|
|
|
|
// No columns selected, duplicating multiple lines
|
|
|
|
|
|
|
|
|
|
const bool isUp = curSel.positionBlock > curSel.anchorBlock;
|
|
|
|
|
const QString commentText =
|
|
|
|
|
(comment && m_commentDefinition.hasSingleLineStyle()) ?
|
|
|
|
|
m_commentDefinition.singleLine : QString();
|
|
|
|
|
|
|
|
|
|
QTextBlock block = cursor.block();
|
|
|
|
|
QString dupText = commentText + block.text() + QLatin1Char('\n');
|
|
|
|
|
|
|
|
|
|
for (int b = curSel.firstBlockNumber(); b < curSel.lastBlockNumber(); ++b) {
|
|
|
|
|
if (isUp) {
|
|
|
|
|
block = block.previous();
|
|
|
|
|
dupText.prepend(commentText + block.text() + QLatin1Char('\n'));
|
|
|
|
|
} else {
|
|
|
|
|
block = block.next();
|
|
|
|
|
dupText.append(commentText + block.text() + QLatin1Char('\n'));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isUp)
|
|
|
|
|
block = cursor.block();
|
|
|
|
|
|
|
|
|
|
cursor.setPosition(block.position() + block.length());
|
|
|
|
|
cursor.insertText(dupText);
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
// Duplicating full block selection with columns
|
|
|
|
|
|
|
|
|
|
// Cannot "duplicate and comment" files without multi-line comment
|
|
|
|
|
if (comment && !m_commentDefinition.hasMultiLineStyle())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const int fc = curSel.firstVisualColumn();
|
|
|
|
|
const int lc = curSel.lastVisualColumn();
|
|
|
|
|
const int l = lc - fc;
|
|
|
|
|
|
|
|
|
|
cursor.beginEditBlock();
|
|
|
|
|
for (int b = curSel.firstBlockNumber(); b <= curSel.lastBlockNumber(); ++b) {
|
|
|
|
|
const QTextBlock &block = m_document->document()->findBlockByNumber(b);
|
|
|
|
|
QString dupText = block.text();
|
|
|
|
|
const int dupTextLength = dupText.length();
|
|
|
|
|
|
|
|
|
|
if (dupTextLength < lc) {
|
|
|
|
|
const QString addSpace(lc - dupTextLength, ' ');
|
|
|
|
|
cursor.setPosition(block.position() + dupTextLength);
|
|
|
|
|
cursor.insertText(addSpace);
|
|
|
|
|
dupText.append(addSpace);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cursor.setPosition(block.position() + lc);
|
|
|
|
|
dupText = dupText.mid(fc, l);
|
|
|
|
|
|
|
|
|
|
if (comment)
|
|
|
|
|
dupText = (m_commentDefinition.multiLineStart + dupText + m_commentDefinition.multiLineEnd);
|
|
|
|
|
cursor.insertText(dupText);
|
|
|
|
|
}
|
|
|
|
|
cursor.endEditBlock();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enableBlockSelection(curSel.positionBlock, curSel.positionColumn,
|
|
|
|
|
curSel.anchorBlock, curSel.anchorColumn);
|
|
|
|
|
|
|
|
|
|
cursor = m_blockSelection.cursor(m_document.data());
|
|
|
|
|
q->doSetTextCursor(cursor, m_blockSelection.hasSelection());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TextEditorWidget::duplicateSelection()
|
|
|
|
|
{
|
|
|
|
|
d->duplicateSelection(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TextEditorWidget::duplicateSelectionAndComment()
|
|
|
|
|
{
|
|
|
|
|
d->duplicateSelection(true);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::deleteLine()
|
2009-05-14 17:24:35 +02:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
d->maybeSelectLine();
|
2009-05-14 17:24:35 +02:00
|
|
|
textCursor().removeSelectedText();
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-26 10:48:48 +02:00
|
|
|
void TextEditorWidget::deleteEndOfLine()
|
|
|
|
|
{
|
|
|
|
|
moveCursor(QTextCursor::EndOfLine, QTextCursor::KeepAnchor);
|
|
|
|
|
textCursor().removeSelectedText();
|
|
|
|
|
setTextCursor(textCursor());
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::deleteEndOfWord()
|
2011-09-26 14:21:19 +02:00
|
|
|
{
|
|
|
|
|
moveCursor(QTextCursor::EndOfWord, QTextCursor::KeepAnchor);
|
|
|
|
|
textCursor().removeSelectedText();
|
|
|
|
|
setTextCursor(textCursor());
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::deleteEndOfWordCamelCase()
|
2011-09-26 14:21:19 +02:00
|
|
|
{
|
|
|
|
|
QTextCursor c = textCursor();
|
2014-07-25 16:23:50 +02:00
|
|
|
d->camelCaseRight(c, QTextCursor::KeepAnchor);
|
2011-09-26 14:21:19 +02:00
|
|
|
c.removeSelectedText();
|
|
|
|
|
setTextCursor(c);
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-26 10:48:48 +02:00
|
|
|
void TextEditorWidget::deleteStartOfLine()
|
|
|
|
|
{
|
|
|
|
|
moveCursor(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
|
|
|
|
|
textCursor().removeSelectedText();
|
|
|
|
|
setTextCursor(textCursor());
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::deleteStartOfWord()
|
2011-09-26 14:21:19 +02:00
|
|
|
{
|
|
|
|
|
moveCursor(QTextCursor::StartOfWord, QTextCursor::KeepAnchor);
|
|
|
|
|
textCursor().removeSelectedText();
|
|
|
|
|
setTextCursor(textCursor());
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::deleteStartOfWordCamelCase()
|
2011-09-26 14:21:19 +02:00
|
|
|
{
|
|
|
|
|
QTextCursor c = textCursor();
|
2014-07-25 16:23:50 +02:00
|
|
|
d->camelCaseLeft(c, QTextCursor::KeepAnchor);
|
2011-09-26 14:21:19 +02:00
|
|
|
c.removeSelectedText();
|
|
|
|
|
setTextCursor(c);
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-23 15:56:45 +02:00
|
|
|
void TextEditorWidgetPrivate::setExtraSelections(Id kind, const QList<QTextEdit::ExtraSelection> &selections)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-10-21 17:50:10 +02:00
|
|
|
if (selections.isEmpty() && m_extraSelections[kind].isEmpty())
|
2008-12-05 13:19:57 +01:00
|
|
|
return;
|
2014-10-21 17:50:10 +02:00
|
|
|
m_extraSelections[kind] = selections;
|
2008-12-05 13:19:57 +01:00
|
|
|
|
2014-10-21 17:50:10 +02:00
|
|
|
if (kind == TextEditorWidget::CodeSemanticsSelection) {
|
|
|
|
|
m_overlay->clear();
|
|
|
|
|
foreach (const QTextEdit::ExtraSelection &selection, m_extraSelections[kind]) {
|
|
|
|
|
m_overlay->addOverlaySelection(selection.cursor,
|
2009-12-03 18:35:36 +01:00
|
|
|
selection.format.background().color(),
|
|
|
|
|
selection.format.background().color(),
|
2010-01-19 12:28:50 +01:00
|
|
|
TextEditorOverlay::LockSize);
|
2009-11-25 15:55:45 +01:00
|
|
|
}
|
2014-10-21 17:50:10 +02:00
|
|
|
m_overlay->setVisible(!m_overlay->isEmpty());
|
|
|
|
|
} else if (kind == TextEditorWidget::SnippetPlaceholderSelection) {
|
|
|
|
|
m_snippetOverlay->mangle();
|
|
|
|
|
m_snippetOverlay->clear();
|
|
|
|
|
foreach (const QTextEdit::ExtraSelection &selection, m_extraSelections[kind]) {
|
|
|
|
|
m_snippetOverlay->addOverlaySelection(selection.cursor,
|
2010-01-19 12:28:50 +01:00
|
|
|
selection.format.background().color(),
|
|
|
|
|
selection.format.background().color(),
|
|
|
|
|
TextEditorOverlay::ExpandBegin);
|
|
|
|
|
}
|
2014-10-21 17:50:10 +02:00
|
|
|
m_snippetOverlay->mapEquivalentSelections();
|
|
|
|
|
m_snippetOverlay->setVisible(!m_snippetOverlay->isEmpty());
|
2009-11-25 15:55:45 +01:00
|
|
|
} else {
|
|
|
|
|
QList<QTextEdit::ExtraSelection> all;
|
2014-10-21 17:50:10 +02:00
|
|
|
for (auto i = m_extraSelections.constBegin(); i != m_extraSelections.constEnd(); ++i) {
|
|
|
|
|
if (i.key() == TextEditorWidget::CodeSemanticsSelection
|
|
|
|
|
|| i.key() == TextEditorWidget::SnippetPlaceholderSelection)
|
2009-11-25 15:55:45 +01:00
|
|
|
continue;
|
2014-10-21 17:50:10 +02:00
|
|
|
all += i.value();
|
2009-11-25 15:55:45 +01:00
|
|
|
}
|
2014-10-21 17:50:10 +02:00
|
|
|
q->QPlainTextEdit::setExtraSelections(all);
|
2009-11-25 15:55:45 +01:00
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2015-02-03 23:46:35 +02:00
|
|
|
void TextEditorWidget::setExtraSelections(Id kind, const QList<QTextEdit::ExtraSelection> &selections)
|
2014-10-21 17:50:10 +02:00
|
|
|
{
|
2015-06-23 15:56:45 +02:00
|
|
|
d->setExtraSelections(kind, selections);
|
2014-10-21 17:50:10 +02:00
|
|
|
}
|
|
|
|
|
|
2015-02-03 23:46:35 +02:00
|
|
|
QList<QTextEdit::ExtraSelection> TextEditorWidget::extraSelections(Id kind) const
|
2014-10-21 17:50:10 +02:00
|
|
|
{
|
2015-06-23 15:56:45 +02:00
|
|
|
return d->m_extraSelections.value(kind);
|
2014-10-21 17:50:10 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
QString TextEditorWidget::extraSelectionTooltip(int pos) const
|
2009-11-30 17:23:31 +01:00
|
|
|
{
|
2014-10-21 17:50:10 +02:00
|
|
|
foreach (const QList<QTextEdit::ExtraSelection> &sel, d->m_extraSelections) {
|
2009-11-30 17:23:31 +01:00
|
|
|
for (int j = 0; j < sel.size(); ++j) {
|
|
|
|
|
const QTextEdit::ExtraSelection &s = sel.at(j);
|
|
|
|
|
if (s.cursor.selectionStart() <= pos
|
|
|
|
|
&& s.cursor.selectionEnd() >= pos
|
|
|
|
|
&& !s.format.toolTip().isEmpty())
|
|
|
|
|
return s.format.toolTip();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return QString();
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
// the blocks list must be sorted
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setIfdefedOutBlocks(const QList<BlockRange> &blocks)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
QTextDocument *doc = document();
|
2014-09-26 09:14:03 +02:00
|
|
|
TextDocumentLayout *documentLayout = qobject_cast<TextDocumentLayout*>(doc->documentLayout());
|
2008-12-09 15:25:01 +01:00
|
|
|
QTC_ASSERT(documentLayout, return);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
bool needUpdate = false;
|
|
|
|
|
|
|
|
|
|
QTextBlock block = doc->firstBlock();
|
|
|
|
|
|
|
|
|
|
int rangeNumber = 0;
|
2009-09-25 17:22:10 +02:00
|
|
|
int braceDepthDelta = 0;
|
2008-12-02 12:01:29 +01:00
|
|
|
while (block.isValid()) {
|
2009-09-25 17:22:10 +02:00
|
|
|
bool cleared = false;
|
|
|
|
|
bool set = false;
|
2008-12-02 12:01:29 +01:00
|
|
|
if (rangeNumber < blocks.size()) {
|
|
|
|
|
const BlockRange &range = blocks.at(rangeNumber);
|
2013-08-09 11:43:20 +02:00
|
|
|
if (block.position() >= range.first()
|
|
|
|
|
&& ((block.position() + block.length() - 1) <= range.last() || !range.last()))
|
2014-09-26 09:14:03 +02:00
|
|
|
set = TextDocumentLayout::setIfdefedOut(block);
|
Remove braces for single lines of conditions
#!/usr/bin/env ruby
Dir.glob('**/*.cpp') { |file|
# skip ast (excluding paste, astpath, and canv'ast'imer)
next if file =~ /ast[^eip]|keywords\.|qualifiers|preprocessor|names.cpp/i
s = File.read(file)
next if s.include?('qlalr')
orig = s.dup
s.gsub!(/\n *if [^\n]*{\n[^\n]*\n\s+}(\s+else if [^\n]* {\n[^\n]*\n\s+})*(\s+else {\n[^\n]*\n\s+})?\n/m) { |m|
res = $&
if res =~ /^\s*(\/\/|[A-Z_]{3,})/ # C++ comment or macro (Q_UNUSED, SDEBUG), do not touch braces
res
else
res.gsub!('} else', 'else')
res.gsub!(/\n +} *\n/m, "\n")
res.gsub(/ *{$/, '')
end
}
s.gsub!(/ *$/, '')
File.open(file, 'wb').write(s) if s != orig
}
Change-Id: I3b30ee60df0986f66c02132c65fc38a3fbb6bbdc
Reviewed-by: hjk <qthjk@ovi.com>
2013-01-08 03:32:53 +02:00
|
|
|
else
|
2014-09-26 09:14:03 +02:00
|
|
|
cleared = TextDocumentLayout::clearIfdefedOut(block);
|
2013-08-09 11:43:20 +02:00
|
|
|
if (block.contains(range.last()))
|
2008-12-02 12:01:29 +01:00
|
|
|
++rangeNumber;
|
|
|
|
|
} else {
|
2014-09-26 09:14:03 +02:00
|
|
|
cleared = TextDocumentLayout::clearIfdefedOut(block);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2009-09-25 17:22:10 +02:00
|
|
|
if (cleared || set) {
|
|
|
|
|
needUpdate = true;
|
2014-09-26 09:14:03 +02:00
|
|
|
int delta = TextDocumentLayout::braceDepthDelta(block);
|
2009-09-25 17:22:10 +02:00
|
|
|
if (cleared)
|
|
|
|
|
braceDepthDelta += delta;
|
|
|
|
|
else if (set)
|
|
|
|
|
braceDepthDelta -= delta;
|
|
|
|
|
}
|
|
|
|
|
|
2010-05-20 15:10:26 +02:00
|
|
|
if (braceDepthDelta) {
|
2014-09-26 09:14:03 +02:00
|
|
|
TextDocumentLayout::changeBraceDepth(block,braceDepthDelta);
|
|
|
|
|
TextDocumentLayout::changeFoldingIndent(block, braceDepthDelta); // ### C++ only, refactor!
|
2010-05-20 15:10:26 +02:00
|
|
|
}
|
2009-09-25 17:22:10 +02:00
|
|
|
|
2008-12-02 12:01:29 +01:00
|
|
|
block = block.next();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (needUpdate)
|
|
|
|
|
documentLayout->requestUpdate();
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::format()
|
2009-03-17 16:16:36 +01:00
|
|
|
{
|
|
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
|
cursor.beginEditBlock();
|
2014-01-15 17:09:52 +01:00
|
|
|
d->m_document->autoIndent(cursor);
|
2009-03-17 16:16:36 +01:00
|
|
|
cursor.endEditBlock();
|
|
|
|
|
}
|
2008-12-02 12:01:29 +01:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::rewrapParagraph()
|
2009-07-21 10:37:00 +02:00
|
|
|
{
|
2014-01-09 14:44:25 +01:00
|
|
|
const int paragraphWidth = marginSettings().m_marginColumn;
|
2012-01-05 11:05:28 +01:00
|
|
|
const QRegExp anyLettersOrNumbers = QRegExp(QLatin1String("\\w"));
|
2014-01-17 15:54:59 +01:00
|
|
|
const int tabSize = d->m_document->tabSettings().m_tabSize;
|
2009-07-21 10:37:00 +02:00
|
|
|
|
|
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
|
cursor.beginEditBlock();
|
|
|
|
|
|
|
|
|
|
// Find start of paragraph.
|
|
|
|
|
|
|
|
|
|
while (cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::MoveAnchor)) {
|
|
|
|
|
QTextBlock block = cursor.block();
|
|
|
|
|
QString text = block.text();
|
|
|
|
|
|
|
|
|
|
// If this block is empty, move marker back to previous and terminate.
|
|
|
|
|
if (!text.contains(anyLettersOrNumbers)) {
|
|
|
|
|
cursor.movePosition(QTextCursor::NextBlock, QTextCursor::MoveAnchor);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cursor.movePosition(QTextCursor::StartOfBlock, QTextCursor::MoveAnchor);
|
|
|
|
|
|
|
|
|
|
// Find indent level of current block.
|
|
|
|
|
|
|
|
|
|
int indentLevel = 0;
|
|
|
|
|
QString text = cursor.block().text();
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < text.length(); i++) {
|
|
|
|
|
const QChar ch = text.at(i);
|
|
|
|
|
|
|
|
|
|
if (ch == QLatin1Char(' '))
|
|
|
|
|
indentLevel++;
|
|
|
|
|
else if (ch == QLatin1Char('\t'))
|
|
|
|
|
indentLevel += tabSize - (indentLevel % tabSize);
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If there is a common prefix, it should be kept and expanded to all lines.
|
|
|
|
|
// this allows nice reflowing of doxygen style comments.
|
|
|
|
|
QTextCursor nextBlock = cursor;
|
|
|
|
|
QString commonPrefix;
|
|
|
|
|
|
|
|
|
|
if (nextBlock.movePosition(QTextCursor::NextBlock))
|
|
|
|
|
{
|
|
|
|
|
QString nText = nextBlock.block().text();
|
|
|
|
|
int maxLength = qMin(text.length(), nText.length());
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < maxLength; ++i) {
|
|
|
|
|
const QChar ch = text.at(i);
|
|
|
|
|
|
|
|
|
|
if (ch != nText[i] || ch.isLetterOrNumber())
|
|
|
|
|
break;
|
|
|
|
|
commonPrefix.append(ch);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Find end of paragraph.
|
|
|
|
|
while (cursor.movePosition(QTextCursor::NextBlock, QTextCursor::KeepAnchor)) {
|
|
|
|
|
QString text = cursor.block().text();
|
|
|
|
|
|
|
|
|
|
if (!text.contains(anyLettersOrNumbers))
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QString selectedText = cursor.selectedText();
|
|
|
|
|
|
|
|
|
|
// Preserve initial indent level.or common prefix.
|
|
|
|
|
QString spacing;
|
|
|
|
|
|
|
|
|
|
if (commonPrefix.isEmpty()) {
|
2014-01-17 15:54:59 +01:00
|
|
|
spacing = d->m_document->tabSettings().indentationString(
|
2016-01-13 14:32:23 +01:00
|
|
|
0, indentLevel, 0, textCursor().block());
|
2009-07-21 10:37:00 +02:00
|
|
|
} else {
|
|
|
|
|
spacing = commonPrefix;
|
|
|
|
|
indentLevel = commonPrefix.length();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int currentLength = indentLevel;
|
|
|
|
|
QString result;
|
|
|
|
|
result.append(spacing);
|
|
|
|
|
|
|
|
|
|
// Remove existing instances of any common prefix from paragraph to
|
|
|
|
|
// reflow.
|
|
|
|
|
selectedText.remove(0, commonPrefix.length());
|
|
|
|
|
commonPrefix.prepend(QChar::ParagraphSeparator);
|
|
|
|
|
selectedText.replace(commonPrefix, QLatin1String("\n"));
|
|
|
|
|
|
|
|
|
|
// remove any repeated spaces, trim lines to PARAGRAPH_WIDTH width and
|
|
|
|
|
// keep the same indentation level as first line in paragraph.
|
|
|
|
|
QString currentWord;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < selectedText.length(); ++i) {
|
|
|
|
|
QChar ch = selectedText.at(i);
|
|
|
|
|
if (ch.isSpace()) {
|
|
|
|
|
if (!currentWord.isEmpty()) {
|
|
|
|
|
currentLength += currentWord.length() + 1;
|
|
|
|
|
|
2009-07-21 10:37:00 +02:00
|
|
|
if (currentLength > paragraphWidth) {
|
2009-07-21 10:37:00 +02:00
|
|
|
currentLength = currentWord.length() + 1 + indentLevel;
|
2009-07-21 12:34:56 +02:00
|
|
|
result.chop(1); // remove trailing space
|
2009-07-21 10:37:00 +02:00
|
|
|
result.append(QChar::ParagraphSeparator);
|
|
|
|
|
result.append(spacing);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result.append(currentWord);
|
2009-07-21 12:37:04 +02:00
|
|
|
result.append(QLatin1Char(' '));
|
2009-07-21 10:37:00 +02:00
|
|
|
currentWord.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currentWord.append(ch);
|
|
|
|
|
}
|
2009-07-21 12:34:56 +02:00
|
|
|
result.chop(1);
|
2009-07-21 10:37:00 +02:00
|
|
|
result.append(QChar::ParagraphSeparator);
|
|
|
|
|
|
|
|
|
|
cursor.insertText(result);
|
|
|
|
|
cursor.endEditBlock();
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::unCommentSelection()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-09-01 16:16:44 +02:00
|
|
|
Utils::unCommentSelection(this, d->m_commentDefinition);
|
2009-03-17 16:16:36 +01:00
|
|
|
}
|
|
|
|
|
|
2015-07-01 13:18:49 +09:00
|
|
|
void TextEditorWidget::encourageApply()
|
|
|
|
|
{
|
|
|
|
|
if (!d->m_snippetOverlay->isVisible() || d->m_snippetOverlay->isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
d->m_snippetOverlay->updateEquivalentSelections(textCursor());
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::showEvent(QShowEvent* e)
|
2009-06-17 19:12:19 +02:00
|
|
|
{
|
2014-02-06 12:41:29 +01:00
|
|
|
triggerPendingUpdates();
|
2009-06-17 19:12:19 +02:00
|
|
|
QPlainTextEdit::showEvent(e);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::applyFontSettingsDelayed()
|
2009-06-17 19:12:19 +02:00
|
|
|
{
|
2014-08-01 15:06:28 +02:00
|
|
|
m_fontSettingsNeedsApply = true;
|
|
|
|
|
if (q->isVisible())
|
|
|
|
|
q->triggerPendingUpdates();
|
2014-02-06 12:41:29 +01:00
|
|
|
}
|
|
|
|
|
|
2017-10-06 14:29:13 +02:00
|
|
|
void TextEditorWidgetPrivate::markRemoved(TextMark *mark)
|
|
|
|
|
{
|
|
|
|
|
auto it = m_annotationRects.find(mark->lineNumber() - 1);
|
|
|
|
|
if (it == m_annotationRects.end())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Utils::erase(it.value(), [mark](AnnotationRect rect) {
|
|
|
|
|
return rect.mark == mark;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::triggerPendingUpdates()
|
2014-02-06 12:41:29 +01:00
|
|
|
{
|
|
|
|
|
if (d->m_fontSettingsNeedsApply)
|
2014-01-21 11:26:39 +01:00
|
|
|
applyFontSettings();
|
2014-08-01 23:31:56 +02:00
|
|
|
textDocument()->triggerPendingUpdates();
|
2009-06-17 19:12:19 +02:00
|
|
|
}
|
2009-09-08 13:03:24 +02:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::applyFontSettings()
|
2009-03-17 16:16:36 +01:00
|
|
|
{
|
2014-02-06 12:41:29 +01:00
|
|
|
d->m_fontSettingsNeedsApply = false;
|
2014-08-01 23:31:56 +02:00
|
|
|
const FontSettings &fs = textDocument()->fontSettings();
|
2012-04-26 14:17:42 +02:00
|
|
|
const QTextCharFormat textFormat = fs.toTextCharFormat(C_TEXT);
|
|
|
|
|
const QTextCharFormat selectionFormat = fs.toTextCharFormat(C_SELECTION);
|
|
|
|
|
const QTextCharFormat lineNumberFormat = fs.toTextCharFormat(C_LINE_NUMBER);
|
2009-03-17 16:16:36 +01:00
|
|
|
QFont font(textFormat.font());
|
|
|
|
|
|
|
|
|
|
const QColor foreground = textFormat.foreground().color();
|
|
|
|
|
const QColor background = textFormat.background().color();
|
|
|
|
|
QPalette p = palette();
|
|
|
|
|
p.setColor(QPalette::Text, foreground);
|
|
|
|
|
p.setColor(QPalette::Foreground, foreground);
|
|
|
|
|
p.setColor(QPalette::Base, background);
|
|
|
|
|
p.setColor(QPalette::Highlight, (selectionFormat.background().style() != Qt::NoBrush) ?
|
|
|
|
|
selectionFormat.background().color() :
|
|
|
|
|
QApplication::palette().color(QPalette::Highlight));
|
2010-08-06 12:59:02 +02:00
|
|
|
|
|
|
|
|
p.setBrush(QPalette::HighlightedText, selectionFormat.foreground());
|
|
|
|
|
|
2009-03-17 16:16:36 +01:00
|
|
|
p.setBrush(QPalette::Inactive, QPalette::Highlight, p.highlight());
|
|
|
|
|
p.setBrush(QPalette::Inactive, QPalette::HighlightedText, p.highlightedText());
|
|
|
|
|
setPalette(p);
|
|
|
|
|
setFont(font);
|
2014-08-01 15:06:28 +02:00
|
|
|
d->updateTabStops(); // update tab stops, they depend on the font
|
2009-03-17 16:16:36 +01:00
|
|
|
|
|
|
|
|
// Line numbers
|
2014-01-07 20:38:32 +01:00
|
|
|
QPalette ep;
|
2009-03-17 16:16:36 +01:00
|
|
|
ep.setColor(QPalette::Dark, lineNumberFormat.foreground().color());
|
|
|
|
|
ep.setColor(QPalette::Background, lineNumberFormat.background().style() != Qt::NoBrush ?
|
|
|
|
|
lineNumberFormat.background().color() : background);
|
|
|
|
|
d->m_extraArea->setPalette(ep);
|
|
|
|
|
|
2014-08-01 15:06:28 +02:00
|
|
|
d->slotUpdateExtraAreaWidth(); // Adjust to new font width
|
2014-07-25 16:23:50 +02:00
|
|
|
d->updateHighlights();
|
2009-03-17 16:16:36 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setDisplaySettings(const DisplaySettings &ds)
|
2009-03-17 16:16:36 +01:00
|
|
|
{
|
|
|
|
|
setLineWrapMode(ds.m_textWrapping ? QPlainTextEdit::WidgetWidth : QPlainTextEdit::NoWrap);
|
|
|
|
|
setLineNumbersVisible(ds.m_displayLineNumbers);
|
|
|
|
|
setHighlightCurrentLine(ds.m_highlightCurrentLine);
|
2009-07-16 11:27:02 +02:00
|
|
|
setRevisionsVisible(ds.m_markTextChanges);
|
2010-04-14 11:49:58 +02:00
|
|
|
setCenterOnScroll(ds.m_centerCursorOnScroll);
|
2014-12-26 18:20:17 +03:00
|
|
|
setParenthesesMatchingEnabled(ds.m_highlightMatchingParentheses);
|
2014-08-01 16:41:18 +02:00
|
|
|
d->m_fileEncodingLabelAction->setVisible(ds.m_displayFileEncoding);
|
2009-03-17 16:16:36 +01:00
|
|
|
|
|
|
|
|
if (d->m_displaySettings.m_visualizeWhitespace != ds.m_visualizeWhitespace) {
|
2014-08-01 23:31:56 +02:00
|
|
|
if (SyntaxHighlighter *highlighter = textDocument()->syntaxHighlighter())
|
2009-03-17 16:16:36 +01:00
|
|
|
highlighter->rehighlight();
|
|
|
|
|
QTextOption option = document()->defaultTextOption();
|
|
|
|
|
if (ds.m_visualizeWhitespace)
|
|
|
|
|
option.setFlags(option.flags() | QTextOption::ShowTabsAndSpaces);
|
|
|
|
|
else
|
|
|
|
|
option.setFlags(option.flags() & ~QTextOption::ShowTabsAndSpaces);
|
|
|
|
|
option.setFlags(option.flags() | QTextOption::AddSpaceForLineAndParagraphSeparators);
|
|
|
|
|
document()->setDefaultTextOption(option);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2009-03-17 16:16:36 +01:00
|
|
|
|
|
|
|
|
d->m_displaySettings = ds;
|
2009-05-08 18:24:44 +02:00
|
|
|
if (!ds.m_highlightBlocks) {
|
2010-05-20 15:10:26 +02:00
|
|
|
d->extraAreaHighlightFoldedBlockNumber = -1;
|
2014-09-26 11:37:54 +02:00
|
|
|
d->m_highlightBlocksInfo = TextEditorPrivateHighlightBlocks();
|
2009-05-08 18:24:44 +02:00
|
|
|
}
|
2009-05-28 11:11:31 +02:00
|
|
|
|
2014-08-01 15:06:28 +02:00
|
|
|
d->updateCodeFoldingVisible();
|
2014-07-25 16:23:50 +02:00
|
|
|
d->updateHighlights();
|
2015-06-04 10:43:34 +02:00
|
|
|
d->setupScrollBar();
|
2009-04-29 16:46:54 +02:00
|
|
|
viewport()->update();
|
|
|
|
|
extraArea()->update();
|
2009-03-17 16:16:36 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setMarginSettings(const MarginSettings &ms)
|
2014-01-09 14:44:25 +01:00
|
|
|
{
|
|
|
|
|
setVisibleWrapColumn(ms.m_showMargin ? ms.m_marginColumn : 0);
|
|
|
|
|
d->m_marginSettings = ms;
|
|
|
|
|
|
|
|
|
|
viewport()->update();
|
|
|
|
|
extraArea()->update();
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-30 17:45:48 +02:00
|
|
|
void TextEditorWidget::setBehaviorSettings(const BehaviorSettings &bs)
|
2009-12-08 17:37:40 +01:00
|
|
|
{
|
2011-07-20 13:40:24 +02:00
|
|
|
d->m_behaviorSettings = bs;
|
2009-12-08 17:37:40 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setTypingSettings(const TypingSettings &typingSettings)
|
2011-08-16 10:45:23 +02:00
|
|
|
{
|
|
|
|
|
d->m_document->setTypingSettings(typingSettings);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setStorageSettings(const StorageSettings &storageSettings)
|
2009-03-17 16:16:36 +01:00
|
|
|
{
|
|
|
|
|
d->m_document->setStorageSettings(storageSettings);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-30 17:45:48 +02:00
|
|
|
void TextEditorWidget::setCompletionSettings(const CompletionSettings &completionSettings)
|
2010-05-11 11:33:31 +02:00
|
|
|
{
|
2016-04-14 09:00:05 +02:00
|
|
|
d->m_autoCompleter->setAutoInsertBracketsEnabled(completionSettings.m_autoInsertBrackets);
|
|
|
|
|
d->m_autoCompleter->setSurroundWithBracketsEnabled(completionSettings.m_surroundingAutoBrackets);
|
|
|
|
|
d->m_autoCompleter->setAutoInsertQuotesEnabled(completionSettings.m_autoInsertQuotes);
|
|
|
|
|
d->m_autoCompleter->setSurroundWithQuotesEnabled(completionSettings.m_surroundingAutoQuotes);
|
2016-04-28 12:51:34 +02:00
|
|
|
d->m_animateAutoComplete = completionSettings.m_animateAutoComplete;
|
2016-06-01 10:19:59 +02:00
|
|
|
d->m_highlightAutoComplete = completionSettings.m_highlightAutoComplete;
|
2016-06-10 15:13:38 +02:00
|
|
|
d->m_skipAutoCompletedText = completionSettings.m_skipAutoCompletedText;
|
2016-06-14 08:00:32 +02:00
|
|
|
d->m_removeAutoCompletedText = completionSettings.m_autoRemove;
|
2010-05-11 11:33:31 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setExtraEncodingSettings(const ExtraEncodingSettings &extraEncodingSettings)
|
2011-02-01 14:13:54 +01:00
|
|
|
{
|
|
|
|
|
d->m_document->setExtraEncodingSettings(extraEncodingSettings);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::fold()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
QTextDocument *doc = document();
|
2014-09-26 09:14:03 +02:00
|
|
|
TextDocumentLayout *documentLayout = qobject_cast<TextDocumentLayout*>(doc->documentLayout());
|
2008-12-09 15:25:01 +01:00
|
|
|
QTC_ASSERT(documentLayout, return);
|
2008-12-02 12:01:29 +01:00
|
|
|
QTextBlock block = textCursor().block();
|
2014-09-26 09:14:03 +02:00
|
|
|
if (!(TextDocumentLayout::canFold(block) && block.next().isVisible())) {
|
2010-05-20 15:10:26 +02:00
|
|
|
// find the closest previous block which can fold
|
2014-09-26 09:14:03 +02:00
|
|
|
int indent = TextDocumentLayout::foldingIndent(block);
|
|
|
|
|
while (block.isValid() && (TextDocumentLayout::foldingIndent(block) >= indent || !block.isVisible()))
|
2010-05-20 15:10:26 +02:00
|
|
|
block = block.previous();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
if (block.isValid()) {
|
2014-09-26 09:14:03 +02:00
|
|
|
TextDocumentLayout::doFoldOrUnfold(block, false);
|
2008-12-02 12:01:29 +01:00
|
|
|
d->moveCursorVisible();
|
|
|
|
|
documentLayout->requestUpdate();
|
|
|
|
|
documentLayout->emitDocumentSizeChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::unfold()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
QTextDocument *doc = document();
|
2014-09-26 09:14:03 +02:00
|
|
|
TextDocumentLayout *documentLayout = qobject_cast<TextDocumentLayout*>(doc->documentLayout());
|
2008-12-09 15:25:01 +01:00
|
|
|
QTC_ASSERT(documentLayout, return);
|
2008-12-02 12:01:29 +01:00
|
|
|
QTextBlock block = textCursor().block();
|
|
|
|
|
while (block.isValid() && !block.isVisible())
|
|
|
|
|
block = block.previous();
|
2014-09-26 09:14:03 +02:00
|
|
|
TextDocumentLayout::doFoldOrUnfold(block, true);
|
2008-12-02 12:01:29 +01:00
|
|
|
d->moveCursorVisible();
|
|
|
|
|
documentLayout->requestUpdate();
|
|
|
|
|
documentLayout->emitDocumentSizeChanged();
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::unfoldAll()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
QTextDocument *doc = document();
|
2014-09-26 09:14:03 +02:00
|
|
|
TextDocumentLayout *documentLayout = qobject_cast<TextDocumentLayout*>(doc->documentLayout());
|
2008-12-09 15:25:01 +01:00
|
|
|
QTC_ASSERT(documentLayout, return);
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
QTextBlock block = doc->firstBlock();
|
|
|
|
|
bool makeVisible = true;
|
|
|
|
|
while (block.isValid()) {
|
2014-09-26 09:14:03 +02:00
|
|
|
if (block.isVisible() && TextDocumentLayout::canFold(block) && block.next().isVisible()) {
|
2008-12-02 12:01:29 +01:00
|
|
|
makeVisible = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
block = block.next();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
block = doc->firstBlock();
|
|
|
|
|
|
|
|
|
|
while (block.isValid()) {
|
2014-09-26 09:14:03 +02:00
|
|
|
if (TextDocumentLayout::canFold(block))
|
|
|
|
|
TextDocumentLayout::doFoldOrUnfold(block, makeVisible);
|
2008-12-02 12:01:29 +01:00
|
|
|
block = block.next();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
d->moveCursorVisible();
|
|
|
|
|
documentLayout->requestUpdate();
|
|
|
|
|
documentLayout->emitDocumentSizeChanged();
|
2009-02-17 16:14:52 +01:00
|
|
|
centerCursor();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setReadOnly(bool b)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
QPlainTextEdit::setReadOnly(b);
|
2013-10-30 09:27:32 +01:00
|
|
|
emit readOnlyChanged();
|
2008-12-02 12:01:29 +01:00
|
|
|
if (b)
|
|
|
|
|
setTextInteractionFlags(textInteractionFlags() | Qt::TextSelectableByKeyboard);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::cut()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
if (d->m_inBlockSelectionMode) {
|
|
|
|
|
copy();
|
|
|
|
|
d->removeBlockSelection();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
QPlainTextEdit::cut();
|
2014-07-25 16:23:50 +02:00
|
|
|
d->collectToCircularClipboard();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::selectAll()
|
2012-01-18 12:09:41 +01:00
|
|
|
{
|
2014-09-24 15:04:09 +02:00
|
|
|
if (d->m_inBlockSelectionMode)
|
2016-12-21 08:44:10 +01:00
|
|
|
d->disableBlockSelection(TextEditorWidgetPrivate::NoCursorUpdate);
|
2012-01-18 12:09:41 +01:00
|
|
|
QPlainTextEdit::selectAll();
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::copy()
|
2011-12-13 12:15:30 +01:00
|
|
|
{
|
2014-06-16 14:20:36 +02:00
|
|
|
if (!textCursor().hasSelection() || (d->m_inBlockSelectionMode
|
|
|
|
|
&& d->m_blockSelection.anchorColumn == d->m_blockSelection.positionColumn)) {
|
2011-12-13 12:15:30 +01:00
|
|
|
return;
|
2014-06-16 14:20:36 +02:00
|
|
|
}
|
2011-12-13 12:15:30 +01:00
|
|
|
|
|
|
|
|
QPlainTextEdit::copy();
|
2014-07-25 16:23:50 +02:00
|
|
|
d->collectToCircularClipboard();
|
2013-09-23 11:53:03 +03:00
|
|
|
}
|
2011-12-13 12:15:30 +01:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::paste()
|
2014-06-16 14:20:36 +02:00
|
|
|
{
|
|
|
|
|
QPlainTextEdit::paste();
|
2015-07-01 13:18:49 +09:00
|
|
|
encourageApply();
|
2014-06-16 14:20:36 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::collectToCircularClipboard()
|
2013-09-23 11:53:03 +03:00
|
|
|
{
|
2011-12-13 12:15:30 +01:00
|
|
|
const QMimeData *mimeData = QApplication::clipboard()->mimeData();
|
2013-09-23 11:53:03 +03:00
|
|
|
if (!mimeData)
|
|
|
|
|
return;
|
|
|
|
|
CircularClipboard *circularClipBoard = CircularClipboard::instance();
|
2014-09-26 11:37:54 +02:00
|
|
|
circularClipBoard->collect(TextEditorWidget::duplicateMimeData(mimeData));
|
2013-09-23 11:53:03 +03:00
|
|
|
// We want the latest copied content to be the first one to appear on circular paste.
|
|
|
|
|
circularClipBoard->toLastCollect();
|
2011-12-13 12:15:30 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::circularPaste()
|
2011-12-13 12:15:30 +01:00
|
|
|
{
|
2012-03-12 17:38:54 +01:00
|
|
|
CircularClipboard *circularClipBoard = CircularClipboard::instance();
|
|
|
|
|
if (const QMimeData *clipboardData = QApplication::clipboard()->mimeData()) {
|
2014-09-26 11:37:54 +02:00
|
|
|
circularClipBoard->collect(TextEditorWidget::duplicateMimeData(clipboardData));
|
2012-03-12 17:38:54 +01:00
|
|
|
circularClipBoard->toLastCollect();
|
2011-12-13 12:15:30 +01:00
|
|
|
}
|
|
|
|
|
|
2016-08-18 13:33:08 +02:00
|
|
|
if (circularClipBoard->size() > 1) {
|
|
|
|
|
invokeAssist(QuickFix, d->m_clipboardAssistProvider.data());
|
|
|
|
|
return;
|
|
|
|
|
}
|
2011-12-13 12:15:30 +01:00
|
|
|
|
2012-03-12 17:38:54 +01:00
|
|
|
if (const QMimeData *mimeData = circularClipBoard->next().data()) {
|
2014-09-26 11:37:54 +02:00
|
|
|
QApplication::clipboard()->setMimeData(TextEditorWidget::duplicateMimeData(mimeData));
|
2012-03-12 17:38:54 +01:00
|
|
|
paste();
|
|
|
|
|
}
|
2011-12-13 12:15:30 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::switchUtf8bom()
|
2012-03-02 08:16:23 +08:00
|
|
|
{
|
2014-08-01 23:31:56 +02:00
|
|
|
textDocument()->switchUtf8Bom();
|
2012-03-02 08:16:23 +08:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
QMimeData *TextEditorWidget::createMimeDataFromSelection() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
if (d->m_inBlockSelectionMode) {
|
|
|
|
|
QMimeData *mimeData = new QMimeData;
|
2014-06-16 14:20:36 +02:00
|
|
|
mimeData->setText(d->copyBlockSelection());
|
2008-12-02 12:01:29 +01:00
|
|
|
return mimeData;
|
2010-03-05 11:50:37 +01:00
|
|
|
} else if (textCursor().hasSelection()) {
|
2009-09-29 12:44:00 +02:00
|
|
|
QTextCursor cursor = textCursor();
|
|
|
|
|
QMimeData *mimeData = new QMimeData;
|
2010-03-05 11:50:37 +01:00
|
|
|
|
2013-04-23 09:09:20 +02:00
|
|
|
QString text = plainTextFromSelection(cursor);
|
2009-09-29 12:44:00 +02:00
|
|
|
mimeData->setText(text);
|
|
|
|
|
|
2010-03-05 11:50:37 +01:00
|
|
|
// Copy the selected text as HTML
|
|
|
|
|
{
|
|
|
|
|
// Create a new document from the selected text document fragment
|
|
|
|
|
QTextDocument *tempDocument = new QTextDocument;
|
|
|
|
|
QTextCursor tempCursor(tempDocument);
|
|
|
|
|
tempCursor.insertFragment(cursor.selection());
|
|
|
|
|
|
|
|
|
|
// Apply the additional formats set by the syntax highlighter
|
|
|
|
|
QTextBlock start = document()->findBlock(cursor.selectionStart());
|
2013-04-23 09:09:20 +02:00
|
|
|
QTextBlock last = document()->findBlock(cursor.selectionEnd());
|
|
|
|
|
QTextBlock end = last.next();
|
2010-03-05 11:50:37 +01:00
|
|
|
|
|
|
|
|
const int selectionStart = cursor.selectionStart();
|
|
|
|
|
const int endOfDocument = tempDocument->characterCount() - 1;
|
2013-04-23 09:09:20 +02:00
|
|
|
int removedCount = 0;
|
2010-03-05 11:50:37 +01:00
|
|
|
for (QTextBlock current = start; current.isValid() && current != end; current = current.next()) {
|
2013-04-23 09:09:20 +02:00
|
|
|
if (selectionVisible(current.blockNumber())) {
|
|
|
|
|
const QTextLayout *layout = current.layout();
|
2016-08-03 23:09:08 +03:00
|
|
|
foreach (const QTextLayout::FormatRange &range, layout->formats()) {
|
2013-04-23 09:09:20 +02:00
|
|
|
const int startPosition = current.position() + range.start - selectionStart - removedCount;
|
|
|
|
|
const int endPosition = startPosition + range.length;
|
2014-05-05 16:52:04 +02:00
|
|
|
if (endPosition <= 0 || startPosition >= endOfDocument - removedCount)
|
2013-04-23 09:09:20 +02:00
|
|
|
continue;
|
|
|
|
|
tempCursor.setPosition(qMax(startPosition, 0));
|
2014-05-05 16:52:04 +02:00
|
|
|
tempCursor.setPosition(qMin(endPosition, endOfDocument - removedCount), QTextCursor::KeepAnchor);
|
2013-04-23 09:09:20 +02:00
|
|
|
tempCursor.setCharFormat(range.format);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
const int startPosition = current.position() - start.position() - removedCount;
|
|
|
|
|
int endPosition = startPosition + current.text().count();
|
|
|
|
|
if (current != last)
|
|
|
|
|
endPosition++;
|
|
|
|
|
removedCount += endPosition - startPosition;
|
|
|
|
|
tempCursor.setPosition(startPosition);
|
|
|
|
|
tempCursor.setPosition(endPosition, QTextCursor::KeepAnchor);
|
|
|
|
|
tempCursor.deleteChar();
|
2010-03-05 11:50:37 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Reset the user states since they are not interesting
|
|
|
|
|
for (QTextBlock block = tempDocument->begin(); block.isValid(); block = block.next())
|
|
|
|
|
block.setUserState(-1);
|
|
|
|
|
|
|
|
|
|
// Make sure the text appears pre-formatted
|
|
|
|
|
tempCursor.setPosition(0);
|
|
|
|
|
tempCursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
|
|
|
|
|
QTextBlockFormat blockFormat = tempCursor.blockFormat();
|
|
|
|
|
blockFormat.setNonBreakableLines(true);
|
|
|
|
|
tempCursor.setBlockFormat(blockFormat);
|
|
|
|
|
|
|
|
|
|
mimeData->setHtml(tempCursor.selection().toHtml());
|
|
|
|
|
delete tempDocument;
|
|
|
|
|
}
|
|
|
|
|
|
2009-09-29 12:44:00 +02:00
|
|
|
/*
|
|
|
|
|
Try to figure out whether we are copying an entire block, and store the complete block
|
|
|
|
|
including indentation in the qtcreator.blocktext mimetype.
|
|
|
|
|
*/
|
|
|
|
|
QTextCursor selstart = cursor;
|
|
|
|
|
selstart.setPosition(cursor.selectionStart());
|
|
|
|
|
QTextCursor selend = cursor;
|
|
|
|
|
selend.setPosition(cursor.selectionEnd());
|
|
|
|
|
|
2013-08-08 13:08:20 +02:00
|
|
|
bool startOk = TabSettings::cursorIsAtBeginningOfLine(selstart);
|
2009-09-30 17:10:58 +02:00
|
|
|
bool multipleBlocks = (selend.block() != selstart.block());
|
2009-09-29 12:44:00 +02:00
|
|
|
|
2009-09-30 17:10:58 +02:00
|
|
|
if (startOk && multipleBlocks) {
|
2009-09-29 12:44:00 +02:00
|
|
|
selstart.movePosition(QTextCursor::StartOfBlock);
|
2013-08-08 13:08:20 +02:00
|
|
|
if (TabSettings::cursorIsAtBeginningOfLine(selend))
|
2009-09-30 17:10:58 +02:00
|
|
|
selend.movePosition(QTextCursor::StartOfBlock);
|
2009-09-29 12:44:00 +02:00
|
|
|
cursor.setPosition(selstart.position());
|
|
|
|
|
cursor.setPosition(selend.position(), QTextCursor::KeepAnchor);
|
2013-04-23 09:09:20 +02:00
|
|
|
text = plainTextFromSelection(cursor);
|
2011-12-13 12:15:30 +01:00
|
|
|
mimeData->setData(QLatin1String(kTextBlockMimeType), text.toUtf8());
|
2009-09-29 12:44:00 +02:00
|
|
|
}
|
|
|
|
|
return mimeData;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2009-09-29 12:44:00 +02:00
|
|
|
return 0;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::canInsertFromMimeData(const QMimeData *source) const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
|
|
|
|
return QPlainTextEdit::canInsertFromMimeData(source);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::insertFromMimeData(const QMimeData *source)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2009-09-29 12:44:00 +02:00
|
|
|
if (isReadOnly())
|
|
|
|
|
return;
|
|
|
|
|
|
2009-09-24 13:39:45 +02:00
|
|
|
QString text = source->text();
|
|
|
|
|
if (text.isEmpty())
|
|
|
|
|
return;
|
|
|
|
|
|
2014-08-20 15:31:39 +02:00
|
|
|
if (d->m_codeAssistant.hasContext())
|
|
|
|
|
d->m_codeAssistant.destroyContext();
|
2010-09-24 11:35:36 +02:00
|
|
|
|
2014-06-16 14:20:36 +02:00
|
|
|
if (d->m_inBlockSelectionMode) {
|
|
|
|
|
d->insertIntoBlockSelection(text);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-24 13:02:13 +02:00
|
|
|
if (d->m_snippetOverlay->isVisible() && (text.contains(QLatin1Char('\n'))
|
|
|
|
|
|| text.contains(QLatin1Char('\t')))) {
|
|
|
|
|
d->m_snippetOverlay->hide();
|
2013-08-09 17:45:14 +02:00
|
|
|
d->m_snippetOverlay->mangle();
|
2010-09-24 13:02:13 +02:00
|
|
|
d->m_snippetOverlay->clear();
|
|
|
|
|
}
|
2010-09-24 11:34:10 +02:00
|
|
|
|
2011-08-16 10:45:23 +02:00
|
|
|
const TypingSettings &tps = d->m_document->typingSettings();
|
2009-09-24 13:39:45 +02:00
|
|
|
QTextCursor cursor = textCursor();
|
2011-08-16 10:45:23 +02:00
|
|
|
if (!tps.m_autoIndent) {
|
2009-09-24 13:39:45 +02:00
|
|
|
cursor.beginEditBlock();
|
|
|
|
|
cursor.insertText(text);
|
|
|
|
|
cursor.endEditBlock();
|
|
|
|
|
setTextCursor(cursor);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cursor.beginEditBlock();
|
2009-11-09 15:55:47 +01:00
|
|
|
cursor.removeSelectedText();
|
2009-09-24 13:39:45 +02:00
|
|
|
|
2013-10-08 11:58:18 +03:00
|
|
|
bool insertAtBeginningOfLine = TabSettings::cursorIsAtBeginningOfLine(cursor);
|
2009-09-30 17:10:58 +02:00
|
|
|
|
|
|
|
|
if (insertAtBeginningOfLine
|
2011-12-13 12:15:30 +01:00
|
|
|
&& source->hasFormat(QLatin1String(kTextBlockMimeType))) {
|
|
|
|
|
text = QString::fromUtf8(source->data(QLatin1String(kTextBlockMimeType)));
|
2009-09-29 12:44:00 +02:00
|
|
|
if (text.isEmpty())
|
|
|
|
|
return;
|
2009-09-30 17:10:58 +02:00
|
|
|
}
|
2009-09-29 12:44:00 +02:00
|
|
|
|
2009-09-30 17:10:58 +02:00
|
|
|
int reindentBlockStart = cursor.blockNumber() + (insertAtBeginningOfLine?0:1);
|
2009-09-28 16:22:39 +02:00
|
|
|
|
2009-09-30 17:10:58 +02:00
|
|
|
bool hasFinalNewline = (text.endsWith(QLatin1Char('\n'))
|
|
|
|
|
|| text.endsWith(QChar::ParagraphSeparator)
|
|
|
|
|
|| text.endsWith(QLatin1Char('\r')));
|
2009-09-28 16:22:39 +02:00
|
|
|
|
2009-10-27 15:22:01 +01:00
|
|
|
if (insertAtBeginningOfLine
|
|
|
|
|
&& hasFinalNewline) // since we'll add a final newline, preserve current line's indentation
|
2009-09-30 17:31:12 +02:00
|
|
|
cursor.setPosition(cursor.block().position());
|
2009-09-30 17:10:58 +02:00
|
|
|
|
2009-10-01 12:40:08 +02:00
|
|
|
int cursorPosition = cursor.position();
|
2009-09-30 17:10:58 +02:00
|
|
|
cursor.insertText(text);
|
|
|
|
|
|
|
|
|
|
int reindentBlockEnd = cursor.blockNumber() - (hasFinalNewline?1:0);
|
|
|
|
|
|
2009-09-30 17:31:12 +02:00
|
|
|
if (reindentBlockStart < reindentBlockEnd
|
|
|
|
|
|| (reindentBlockStart == reindentBlockEnd
|
|
|
|
|
&& (!insertAtBeginningOfLine || hasFinalNewline))) {
|
2009-10-01 12:40:08 +02:00
|
|
|
if (insertAtBeginningOfLine && !hasFinalNewline) {
|
|
|
|
|
QTextCursor unnecessaryWhitespace = cursor;
|
|
|
|
|
unnecessaryWhitespace.setPosition(cursorPosition);
|
|
|
|
|
unnecessaryWhitespace.movePosition(QTextCursor::StartOfBlock, QTextCursor::KeepAnchor);
|
2009-09-30 17:10:58 +02:00
|
|
|
unnecessaryWhitespace.removeSelectedText();
|
2009-10-01 12:40:08 +02:00
|
|
|
}
|
2009-09-30 17:10:58 +02:00
|
|
|
QTextCursor c = cursor;
|
|
|
|
|
c.setPosition(cursor.document()->findBlockByNumber(reindentBlockStart).position());
|
|
|
|
|
c.setPosition(cursor.document()->findBlockByNumber(reindentBlockEnd).position(),
|
|
|
|
|
QTextCursor::KeepAnchor);
|
2014-01-15 17:09:52 +01:00
|
|
|
d->m_document->autoReindent(c);
|
2009-09-24 13:39:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cursor.endEditBlock();
|
|
|
|
|
setTextCursor(cursor);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
QMimeData *TextEditorWidget::duplicateMimeData(const QMimeData *source)
|
2011-12-13 12:15:30 +01:00
|
|
|
{
|
|
|
|
|
Q_ASSERT(source);
|
|
|
|
|
|
|
|
|
|
QMimeData *mimeData = new QMimeData;
|
|
|
|
|
mimeData->setText(source->text());
|
|
|
|
|
mimeData->setHtml(source->html());
|
2014-06-16 14:20:36 +02:00
|
|
|
if (source->hasFormat(QLatin1String(kTextBlockMimeType))) {
|
2011-12-13 12:15:30 +01:00
|
|
|
mimeData->setData(QLatin1String(kTextBlockMimeType),
|
|
|
|
|
source->data(QLatin1String(kTextBlockMimeType)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return mimeData;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
QString TextEditorWidget::lineNumber(int blockNumber) const
|
2013-01-20 23:09:09 +01:00
|
|
|
{
|
|
|
|
|
return QString::number(blockNumber + 1);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
int TextEditorWidget::lineNumberDigits() const
|
2013-01-20 23:09:09 +01:00
|
|
|
{
|
|
|
|
|
int digits = 2;
|
|
|
|
|
int max = qMax(1, blockCount());
|
|
|
|
|
while (max >= 100) {
|
|
|
|
|
max /= 10;
|
|
|
|
|
++digits;
|
|
|
|
|
}
|
|
|
|
|
return digits;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::selectionVisible(int blockNumber) const
|
2013-04-09 10:26:31 +02:00
|
|
|
{
|
2013-04-10 19:37:25 +04:00
|
|
|
Q_UNUSED(blockNumber);
|
2013-04-10 13:51:04 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::replacementVisible(int blockNumber) const
|
2013-04-10 13:51:04 +02:00
|
|
|
{
|
|
|
|
|
Q_UNUSED(blockNumber)
|
2013-04-09 10:26:31 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
QColor TextEditorWidget::replacementPenColor(int blockNumber) const
|
2013-06-05 15:25:42 +02:00
|
|
|
{
|
|
|
|
|
Q_UNUSED(blockNumber)
|
|
|
|
|
return QColor();
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setupFallBackEditor(Id id)
|
2014-07-30 11:41:06 +02:00
|
|
|
{
|
2014-09-26 11:37:54 +02:00
|
|
|
TextDocumentPtr doc(new TextDocument(id));
|
2014-08-28 18:46:39 +02:00
|
|
|
doc->setFontSettings(TextEditorSettings::fontSettings());
|
|
|
|
|
setTextDocument(doc);
|
2014-07-30 11:41:06 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::appendStandardContextMenuActions(QMenu *menu)
|
2010-04-26 14:02:09 +02:00
|
|
|
{
|
|
|
|
|
menu->addSeparator();
|
|
|
|
|
|
2014-08-19 14:23:03 +02:00
|
|
|
QAction *a = ActionManager::command(Core::Constants::CUT)->action();
|
2010-04-26 14:02:09 +02:00
|
|
|
if (a && a->isEnabled())
|
|
|
|
|
menu->addAction(a);
|
2014-08-19 14:23:03 +02:00
|
|
|
a = ActionManager::command(Core::Constants::COPY)->action();
|
2010-04-26 14:02:09 +02:00
|
|
|
if (a && a->isEnabled())
|
|
|
|
|
menu->addAction(a);
|
2014-08-19 14:23:03 +02:00
|
|
|
a = ActionManager::command(Core::Constants::PASTE)->action();
|
2010-04-26 14:02:09 +02:00
|
|
|
if (a && a->isEnabled())
|
|
|
|
|
menu->addAction(a);
|
2014-08-19 14:23:03 +02:00
|
|
|
a = ActionManager::command(Constants::CIRCULAR_PASTE)->action();
|
2011-12-13 12:15:30 +01:00
|
|
|
if (a && a->isEnabled())
|
|
|
|
|
menu->addAction(a);
|
2012-03-02 08:16:23 +08:00
|
|
|
|
2014-09-22 18:43:31 +02:00
|
|
|
TextDocument *doc = textDocument();
|
2013-01-24 08:47:31 +01:00
|
|
|
if (doc->codec()->name() == QByteArray("UTF-8") && doc->supportsUtf8Bom()) {
|
2014-08-19 14:23:03 +02:00
|
|
|
a = ActionManager::command(Constants::SWITCH_UTF8BOM)->action();
|
2012-03-02 08:16:23 +08:00
|
|
|
if (a && a->isEnabled()) {
|
|
|
|
|
a->setText(doc->format().hasUtf8Bom ? tr("Delete UTF-8 BOM on Save")
|
|
|
|
|
: tr("Add UTF-8 BOM on Save"));
|
|
|
|
|
menu->addSeparator();
|
|
|
|
|
menu->addAction(a);
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-04-26 14:02:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2014-08-19 00:54:45 +02:00
|
|
|
BaseTextEditor::BaseTextEditor()
|
2014-07-26 12:45:48 +02:00
|
|
|
: d(new BaseTextEditorPrivate)
|
2014-08-19 00:54:45 +02:00
|
|
|
{
|
2014-09-30 17:45:48 +02:00
|
|
|
addContext(Constants::C_TEXTEDITOR);
|
2014-08-19 00:54:45 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
BaseTextEditor::~BaseTextEditor()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-08-20 13:56:57 +02:00
|
|
|
delete m_widget;
|
2014-07-26 12:45:48 +02:00
|
|
|
delete d;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-22 18:43:31 +02:00
|
|
|
TextDocument *BaseTextEditor::textDocument() const
|
2014-07-23 19:10:38 +02:00
|
|
|
{
|
2014-09-26 11:37:54 +02:00
|
|
|
TextEditorWidget *widget = editorWidget();
|
2014-09-02 13:22:56 +02:00
|
|
|
QTC_CHECK(!widget->d->m_document.isNull());
|
|
|
|
|
return widget->d->m_document.data();
|
2014-08-20 13:56:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BaseTextEditor::addContext(Id id)
|
|
|
|
|
{
|
|
|
|
|
m_context.add(id);
|
2014-07-23 19:10:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IDocument *BaseTextEditor::document()
|
|
|
|
|
{
|
2014-08-20 13:56:57 +02:00
|
|
|
return textDocument();
|
2014-07-23 19:10:38 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
QWidget *BaseTextEditor::toolBar()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-08-20 13:56:57 +02:00
|
|
|
return editorWidget()->d->m_toolBar;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2016-10-13 13:11:07 +02:00
|
|
|
QAction * TextEditorWidget::insertExtraToolBarWidget(TextEditorWidget::Side side,
|
|
|
|
|
QWidget *widget)
|
2011-02-25 15:58:02 +01:00
|
|
|
{
|
|
|
|
|
if (widget->sizePolicy().horizontalPolicy() & QSizePolicy::ExpandFlag) {
|
2014-07-26 12:45:48 +02:00
|
|
|
if (d->m_stretchWidget)
|
|
|
|
|
d->m_stretchWidget->deleteLater();
|
|
|
|
|
d->m_stretchWidget = 0;
|
2011-02-25 15:58:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (side == Right)
|
2016-10-13 13:11:07 +02:00
|
|
|
return d->m_toolBar->insertWidget(d->m_cursorPositionLabelAction, widget);
|
2011-02-25 15:58:02 +01:00
|
|
|
else
|
2016-10-13 13:11:07 +02:00
|
|
|
return d->m_toolBar->insertWidget(d->m_toolBar->actions().first(), widget);
|
2011-02-25 15:58:02 +01:00
|
|
|
}
|
|
|
|
|
|
2016-06-01 10:19:59 +02:00
|
|
|
void TextEditorWidget::keepAutoCompletionHighlight(bool keepHighlight)
|
|
|
|
|
{
|
|
|
|
|
d->m_keepAutoCompletionHighlight = keepHighlight;
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-10 15:13:38 +02:00
|
|
|
void TextEditorWidget::setAutoCompleteSkipPosition(const QTextCursor &cursor)
|
|
|
|
|
{
|
|
|
|
|
QTextCursor tc = cursor;
|
2017-07-17 14:32:59 +02:00
|
|
|
// Create a selection of the next character but keep the current position, otherwise
|
|
|
|
|
// the cursor would be removed from the list of automatically inserted text positions
|
|
|
|
|
tc.movePosition(QTextCursor::NextCharacter);
|
|
|
|
|
tc.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
|
2016-06-10 15:13:38 +02:00
|
|
|
d->autocompleterHighlight(tc);
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
int BaseTextEditor::currentLine() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-08-20 13:56:57 +02:00
|
|
|
return editorWidget()->textCursor().blockNumber() + 1;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
int BaseTextEditor::currentColumn() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-08-20 13:56:57 +02:00
|
|
|
QTextCursor cursor = editorWidget()->textCursor();
|
2008-12-02 12:01:29 +01:00
|
|
|
return cursor.position() - cursor.block().position() + 1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-23 19:10:38 +02:00
|
|
|
void BaseTextEditor::gotoLine(int line, int column, bool centerLine)
|
|
|
|
|
{
|
2014-08-20 13:56:57 +02:00
|
|
|
editorWidget()->gotoLine(line, column, centerLine);
|
2014-07-23 19:10:38 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
int BaseTextEditor::columnCount() const
|
2010-12-13 15:17:33 +01:00
|
|
|
{
|
2014-08-20 13:56:57 +02:00
|
|
|
return editorWidget()->columnCount();
|
2010-12-13 15:17:33 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
int BaseTextEditor::rowCount() const
|
2010-12-13 15:17:33 +01:00
|
|
|
{
|
2014-08-20 13:56:57 +02:00
|
|
|
return editorWidget()->rowCount();
|
2010-12-13 15:17:33 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-30 17:45:48 +02:00
|
|
|
int BaseTextEditor::position(TextPositionOperation posOp, int at) const
|
2014-07-23 19:10:38 +02:00
|
|
|
{
|
2014-08-20 13:56:57 +02:00
|
|
|
return editorWidget()->position(posOp, at);
|
2014-07-23 19:10:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BaseTextEditor::convertPosition(int pos, int *line, int *column) const
|
|
|
|
|
{
|
2014-08-20 13:56:57 +02:00
|
|
|
editorWidget()->convertPosition(pos, line, column);
|
2014-07-23 19:10:38 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
QString BaseTextEditor::selectedText() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-08-20 13:56:57 +02:00
|
|
|
return editorWidget()->selectedText();
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void BaseTextEditor::remove(int length)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-09-03 08:35:58 +02:00
|
|
|
editorWidget()->remove(length);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::remove(int length)
|
2014-09-03 08:35:58 +02:00
|
|
|
{
|
|
|
|
|
QTextCursor tc = textCursor();
|
2008-12-02 12:01:29 +01:00
|
|
|
tc.setPosition(tc.position() + length, QTextCursor::KeepAnchor);
|
|
|
|
|
tc.removeSelectedText();
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void BaseTextEditor::insert(const QString &string)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-08-20 13:56:57 +02:00
|
|
|
editorWidget()->insertPlainText(string);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void BaseTextEditor::replace(int length, const QString &string)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-09-03 08:35:58 +02:00
|
|
|
editorWidget()->replace(length, string);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::replace(int length, const QString &string)
|
2014-09-03 08:35:58 +02:00
|
|
|
{
|
|
|
|
|
QTextCursor tc = textCursor();
|
2008-12-02 12:01:29 +01:00
|
|
|
tc.setPosition(tc.position() + length, QTextCursor::KeepAnchor);
|
|
|
|
|
tc.insertText(string);
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void BaseTextEditor::setCursorPosition(int pos)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-09-03 08:35:58 +02:00
|
|
|
editorWidget()->setCursorPosition(pos);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setCursorPosition(int pos)
|
2014-09-03 08:35:58 +02:00
|
|
|
{
|
2016-12-21 08:44:10 +01:00
|
|
|
if (d->m_inBlockSelectionMode)
|
|
|
|
|
d->disableBlockSelection(TextEditorWidgetPrivate::NoCursorUpdate);
|
2014-09-03 08:35:58 +02:00
|
|
|
QTextCursor tc = textCursor();
|
2008-12-02 12:01:29 +01:00
|
|
|
tc.setPosition(pos);
|
2014-09-03 08:35:58 +02:00
|
|
|
setTextCursor(tc);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2015-08-24 13:39:40 +02:00
|
|
|
QToolBar *TextEditorWidget::toolBar()
|
|
|
|
|
{
|
|
|
|
|
return d->m_toolBar;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
void BaseTextEditor::select(int toPos)
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-08-20 13:56:57 +02:00
|
|
|
editorWidget()->setBlockSelection(false);
|
|
|
|
|
QTextCursor tc = editorWidget()->textCursor();
|
2008-12-02 12:01:29 +01:00
|
|
|
tc.setPosition(toPos, QTextCursor::KeepAnchor);
|
2014-08-20 13:56:57 +02:00
|
|
|
editorWidget()->setTextCursor(tc);
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::updateCursorPosition()
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-08-01 16:41:18 +02:00
|
|
|
const QTextCursor cursor = q->textCursor();
|
2008-12-02 12:01:29 +01:00
|
|
|
const QTextBlock block = cursor.block();
|
|
|
|
|
const int line = block.blockNumber() + 1;
|
2009-06-09 19:45:18 +02:00
|
|
|
const int column = cursor.position() - block.position();
|
2014-08-01 16:41:18 +02:00
|
|
|
m_cursorPositionLabel->show();
|
2015-03-24 10:57:11 +03:00
|
|
|
m_cursorPositionLabel->setText(TextEditorWidget::tr("Line: %1, Col: %2").arg(line)
|
2014-08-01 23:31:56 +02:00
|
|
|
.arg(q->textDocument()->tabSettings().columnAt(block.text(),
|
2014-01-17 15:54:59 +01:00
|
|
|
column)+1),
|
2015-03-24 10:57:11 +03:00
|
|
|
TextEditorWidget::tr("Line: 9999, Col: 999"));
|
2014-09-30 13:08:05 +02:00
|
|
|
m_contextHelpId.clear();
|
2008-12-02 12:01:29 +01:00
|
|
|
|
|
|
|
|
if (!block.isVisible())
|
2014-08-01 16:41:18 +02:00
|
|
|
q->ensureCursorVisible();
|
2013-04-18 10:06:13 +02:00
|
|
|
}
|
|
|
|
|
|
2011-02-21 16:02:26 +01:00
|
|
|
QString BaseTextEditor::contextHelpId() const
|
2008-12-02 12:01:29 +01:00
|
|
|
{
|
2014-09-30 13:08:05 +02:00
|
|
|
return editorWidget()->contextHelpId();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BaseTextEditor::setContextHelpId(const QString &id)
|
|
|
|
|
{
|
|
|
|
|
IEditor::setContextHelpId(id);
|
|
|
|
|
editorWidget()->setContextHelpId(id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString TextEditorWidget::contextHelpId()
|
|
|
|
|
{
|
|
|
|
|
if (d->m_contextHelpId.isEmpty() && !d->m_hoverHandlers.isEmpty())
|
|
|
|
|
d->m_contextHelpId = d->m_hoverHandlers.first()->contextHelpId(this, textCursor().position());
|
|
|
|
|
return d->m_contextHelpId;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TextEditorWidget::setContextHelpId(const QString &id)
|
|
|
|
|
{
|
|
|
|
|
d->m_contextHelpId = id;
|
2008-12-02 12:01:29 +01:00
|
|
|
}
|
2010-07-02 13:47:14 +02:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
RefactorMarkers TextEditorWidget::refactorMarkers() const
|
2011-08-12 09:25:01 +02:00
|
|
|
{
|
|
|
|
|
return d->m_refactorOverlay->markers();
|
|
|
|
|
}
|
2010-07-02 13:47:14 +02:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setRefactorMarkers(const RefactorMarkers &markers)
|
2010-07-02 13:47:14 +02:00
|
|
|
{
|
2010-11-01 16:29:45 +01:00
|
|
|
foreach (const RefactorMarker &marker, d->m_refactorOverlay->markers())
|
2010-07-02 13:47:14 +02:00
|
|
|
requestBlockUpdate(marker.cursor.block());
|
|
|
|
|
d->m_refactorOverlay->setMarkers(markers);
|
2010-11-01 16:29:45 +01:00
|
|
|
foreach (const RefactorMarker &marker, markers)
|
2010-07-02 13:47:14 +02:00
|
|
|
requestBlockUpdate(marker.cursor.block());
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
TextBlockSelection::TextBlockSelection(const TextBlockSelection &other)
|
2010-08-05 15:01:20 +02:00
|
|
|
{
|
2014-06-16 14:20:36 +02:00
|
|
|
positionBlock = other.positionBlock;
|
|
|
|
|
positionColumn = other.positionColumn;
|
|
|
|
|
anchorBlock = other.anchorBlock;
|
|
|
|
|
anchorColumn = other.anchorColumn;
|
|
|
|
|
}
|
2010-08-05 15:01:20 +02:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextBlockSelection::clear()
|
2014-06-16 14:20:36 +02:00
|
|
|
{
|
|
|
|
|
positionColumn = positionBlock = anchorColumn = anchorBlock = 0;
|
2010-08-05 15:01:20 +02:00
|
|
|
}
|
|
|
|
|
|
2014-06-16 14:20:36 +02:00
|
|
|
// returns a cursor which always has the complete selection
|
2014-09-26 11:37:54 +02:00
|
|
|
QTextCursor TextBlockSelection::selection(const TextDocument *baseTextDocument) const
|
2014-02-26 11:16:38 +01:00
|
|
|
{
|
2014-06-16 14:20:36 +02:00
|
|
|
return cursor(baseTextDocument, true);
|
2014-02-26 11:16:38 +01:00
|
|
|
}
|
|
|
|
|
|
2014-06-16 14:20:36 +02:00
|
|
|
// returns a cursor which always has the correct position and anchor
|
2014-09-26 11:37:54 +02:00
|
|
|
QTextCursor TextBlockSelection::cursor(const TextDocument *baseTextDocument) const
|
2010-08-05 15:01:20 +02:00
|
|
|
{
|
2014-06-16 14:20:36 +02:00
|
|
|
return cursor(baseTextDocument, false);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
QTextCursor TextBlockSelection::cursor(const TextDocument *baseTextDocument,
|
2014-06-16 14:20:36 +02:00
|
|
|
bool fullSelection) const
|
|
|
|
|
{
|
|
|
|
|
if (!baseTextDocument)
|
|
|
|
|
return QTextCursor();
|
|
|
|
|
QTextDocument *document = baseTextDocument->document();
|
|
|
|
|
const TabSettings &ts = baseTextDocument->tabSettings();
|
|
|
|
|
|
|
|
|
|
int selectionAnchorColumn;
|
|
|
|
|
int selectionPositionColumn;
|
|
|
|
|
if (anchorBlock == positionBlock || !fullSelection) {
|
|
|
|
|
selectionAnchorColumn = anchorColumn;
|
|
|
|
|
selectionPositionColumn = positionColumn;
|
|
|
|
|
} else if (anchorBlock == firstBlockNumber()){
|
|
|
|
|
selectionAnchorColumn = firstVisualColumn();
|
|
|
|
|
selectionPositionColumn = lastVisualColumn();
|
2010-08-05 15:01:20 +02:00
|
|
|
} else {
|
2014-06-16 14:20:36 +02:00
|
|
|
selectionAnchorColumn = lastVisualColumn();
|
|
|
|
|
selectionPositionColumn = firstVisualColumn();
|
2010-08-05 15:01:20 +02:00
|
|
|
}
|
2014-06-16 14:20:36 +02:00
|
|
|
|
|
|
|
|
QTextCursor cursor(document);
|
|
|
|
|
|
|
|
|
|
const QTextBlock &anchorTextBlock = document->findBlockByNumber(anchorBlock);
|
|
|
|
|
const int anchorPosition = anchorTextBlock.position()
|
|
|
|
|
+ ts.positionAtColumn(anchorTextBlock.text(), selectionAnchorColumn);
|
|
|
|
|
|
|
|
|
|
const QTextBlock &positionTextBlock = document->findBlockByNumber(positionBlock);
|
|
|
|
|
const int cursorPosition = positionTextBlock.position()
|
|
|
|
|
+ ts.positionAtColumn(positionTextBlock.text(), selectionPositionColumn);
|
|
|
|
|
|
|
|
|
|
cursor.setPosition(anchorPosition);
|
|
|
|
|
cursor.setPosition(cursorPosition, QTextCursor::KeepAnchor);
|
2010-08-05 15:01:20 +02:00
|
|
|
return cursor;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextBlockSelection::fromPostition(int positionBlock, int positionColumn,
|
2014-06-16 14:20:36 +02:00
|
|
|
int anchorBlock, int anchorColumn)
|
2010-08-05 15:01:20 +02:00
|
|
|
{
|
2014-06-16 14:20:36 +02:00
|
|
|
this->positionBlock = positionBlock;
|
|
|
|
|
this->positionColumn = positionColumn;
|
|
|
|
|
this->anchorBlock = anchorBlock;
|
|
|
|
|
this->anchorColumn = anchorColumn;
|
2010-08-05 15:01:20 +02:00
|
|
|
}
|
2014-06-16 14:20:36 +02:00
|
|
|
|
2015-04-21 12:39:01 +02:00
|
|
|
bool TextEditorWidget::inFindScope(const QTextCursor &cursor)
|
2010-08-05 15:01:20 +02:00
|
|
|
{
|
|
|
|
|
if (cursor.isNull())
|
|
|
|
|
return false;
|
|
|
|
|
return inFindScope(cursor.selectionStart(), cursor.selectionEnd());
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-21 12:39:01 +02:00
|
|
|
bool TextEditorWidget::inFindScope(int selectionStart, int selectionEnd)
|
2010-08-05 15:01:20 +02:00
|
|
|
{
|
2015-04-21 12:39:01 +02:00
|
|
|
if (d->m_findScopeStart.isNull())
|
2010-08-05 15:01:20 +02:00
|
|
|
return true; // no scope, everything is included
|
2015-04-21 12:39:01 +02:00
|
|
|
if (selectionStart < d->m_findScopeStart.position())
|
2010-08-05 15:01:20 +02:00
|
|
|
return false;
|
2015-04-21 12:39:01 +02:00
|
|
|
if (selectionEnd > d->m_findScopeEnd.position())
|
2010-08-05 15:01:20 +02:00
|
|
|
return false;
|
2015-04-21 12:39:01 +02:00
|
|
|
if (d->m_findScopeVerticalBlockSelectionFirstColumn < 0)
|
2010-08-05 15:01:20 +02:00
|
|
|
return true;
|
2015-04-21 12:39:01 +02:00
|
|
|
QTextBlock block = document()->findBlock(selectionStart);
|
|
|
|
|
if (block != document()->findBlock(selectionEnd))
|
2010-08-05 15:01:20 +02:00
|
|
|
return false;
|
|
|
|
|
QString text = block.text();
|
2015-04-21 12:39:01 +02:00
|
|
|
const TabSettings &ts = d->m_document->tabSettings();
|
|
|
|
|
int startPosition = ts.positionAtColumn(text, d->m_findScopeVerticalBlockSelectionFirstColumn);
|
|
|
|
|
int endPosition = ts.positionAtColumn(text, d->m_findScopeVerticalBlockSelectionLastColumn);
|
2010-08-05 15:01:20 +02:00
|
|
|
if (selectionStart - block.position() < startPosition)
|
|
|
|
|
return false;
|
|
|
|
|
if (selectionEnd - block.position() > endPosition)
|
|
|
|
|
return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setBlockSelection(bool on)
|
2010-09-13 13:52:50 +02:00
|
|
|
{
|
2014-02-24 15:04:47 +01:00
|
|
|
if (d->m_inBlockSelectionMode == on)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (on)
|
2014-06-16 14:20:36 +02:00
|
|
|
d->enableBlockSelection(textCursor());
|
|
|
|
|
else
|
2016-12-21 08:44:10 +01:00
|
|
|
d->disableBlockSelection(TextEditorWidgetPrivate::CursorUpdateClearSelection);
|
2010-09-13 13:52:50 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setBlockSelection(int positionBlock, int positionColumn,
|
2014-06-16 14:20:36 +02:00
|
|
|
int anchhorBlock, int anchorColumn)
|
2010-09-13 13:52:50 +02:00
|
|
|
{
|
2014-06-16 14:20:36 +02:00
|
|
|
d->enableBlockSelection(positionBlock, positionColumn, anchhorBlock, anchorColumn);
|
2010-09-13 13:52:50 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::setBlockSelection(const QTextCursor &cursor)
|
2010-08-05 15:01:20 +02:00
|
|
|
{
|
2014-06-16 14:20:36 +02:00
|
|
|
d->enableBlockSelection(cursor);
|
|
|
|
|
}
|
2010-08-05 15:01:20 +02:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
QTextCursor TextEditorWidget::blockSelection() const
|
2014-06-16 14:20:36 +02:00
|
|
|
{
|
|
|
|
|
return d->m_blockSelection.cursor(d->m_document.data());
|
|
|
|
|
}
|
2010-08-05 15:01:20 +02:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::hasBlockSelection() const
|
2014-06-16 14:20:36 +02:00
|
|
|
{
|
|
|
|
|
return d->m_inBlockSelectionMode;
|
2010-08-05 15:01:20 +02:00
|
|
|
}
|
2010-12-13 15:17:33 +01:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::updateTabStops()
|
2014-01-17 17:09:15 +01:00
|
|
|
{
|
|
|
|
|
// Although the tab stop is stored as qreal the API from QPlainTextEdit only allows it
|
|
|
|
|
// to be set as an int. A work around is to access directly the QTextOption.
|
2014-08-01 15:06:28 +02:00
|
|
|
qreal charWidth = QFontMetricsF(q->font()).width(QLatin1Char(' '));
|
|
|
|
|
QTextOption option = q->document()->defaultTextOption();
|
|
|
|
|
option.setTabStop(charWidth * m_document->tabSettings().m_tabSize);
|
|
|
|
|
q->document()->setDefaultTextOption(option);
|
2014-01-17 17:09:15 +01:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
int TextEditorWidget::columnCount() const
|
2010-12-13 15:17:33 +01:00
|
|
|
{
|
|
|
|
|
QFontMetricsF fm(font());
|
|
|
|
|
return viewport()->rect().width() / fm.width(QLatin1Char('x'));
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
int TextEditorWidget::rowCount() const
|
2010-12-13 15:17:33 +01:00
|
|
|
{
|
|
|
|
|
QFontMetricsF fm(font());
|
|
|
|
|
return viewport()->rect().height() / fm.lineSpacing();
|
|
|
|
|
}
|
2011-02-25 11:10:42 +01:00
|
|
|
|
|
|
|
|
/**
|
2013-10-07 13:34:40 +02:00
|
|
|
Helper function to transform a selected text. If nothing is selected at the moment
|
2011-02-25 11:10:42 +01:00
|
|
|
the word under the cursor is used.
|
2013-10-07 13:34:40 +02:00
|
|
|
The type of the transformation is determined by the function pointer given.
|
2011-02-25 11:10:42 +01:00
|
|
|
|
2013-10-07 13:34:40 +02:00
|
|
|
@param method pointer to the QString function to use for the transformation
|
2011-02-25 11:10:42 +01:00
|
|
|
|
|
|
|
|
@see uppercaseSelection, lowercaseSelection
|
|
|
|
|
*/
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::transformSelection(TransformationMethod method)
|
2011-02-25 11:10:42 +01:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
if (q->hasBlockSelection()) {
|
2012-08-17 16:35:48 +02:00
|
|
|
transformBlockSelection(method);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2011-02-25 11:10:42 +01:00
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
QTextCursor cursor = q->textCursor();
|
2011-02-25 11:10:42 +01:00
|
|
|
int pos = cursor.position();
|
|
|
|
|
int anchor = cursor.anchor();
|
|
|
|
|
|
|
|
|
|
if (!cursor.hasSelection()) {
|
|
|
|
|
// if nothing is selected, select the word over the cursor
|
|
|
|
|
cursor.select(QTextCursor::WordUnderCursor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString text = cursor.selectedText();
|
2014-07-29 22:02:56 -07:00
|
|
|
QString transformedText = method(text);
|
2011-02-25 11:10:42 +01:00
|
|
|
|
|
|
|
|
if (transformedText == text) {
|
|
|
|
|
// if the transformation does not do anything to the selection, do no create an undo step
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
cursor.insertText(transformedText);
|
|
|
|
|
|
|
|
|
|
// (re)select the changed text
|
|
|
|
|
// Note: this assumes the transformation did not change the length,
|
|
|
|
|
cursor.setPosition(anchor);
|
|
|
|
|
cursor.setPosition(pos, QTextCursor::KeepAnchor);
|
2014-07-25 16:23:50 +02:00
|
|
|
q->setTextCursor(cursor);
|
2011-02-25 11:10:42 +01:00
|
|
|
}
|
2011-04-06 14:55:26 +02:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidgetPrivate::transformBlockSelection(TransformationMethod method)
|
2012-08-17 16:35:48 +02:00
|
|
|
{
|
2014-07-25 16:23:50 +02:00
|
|
|
QTextCursor cursor = q->textCursor();
|
|
|
|
|
const TabSettings &ts = m_document->tabSettings();
|
2014-02-26 11:16:38 +01:00
|
|
|
|
|
|
|
|
// saved to restore the blockselection
|
2014-07-25 16:23:50 +02:00
|
|
|
const int positionColumn = m_blockSelection.positionColumn;
|
|
|
|
|
const int positionBlock = m_blockSelection.positionBlock;
|
|
|
|
|
const int anchorColumn = m_blockSelection.anchorColumn;
|
|
|
|
|
const int anchorBlock = m_blockSelection.anchorBlock;
|
2014-02-26 11:16:38 +01:00
|
|
|
|
2014-07-25 16:23:50 +02:00
|
|
|
QTextBlock block = m_document->document()->findBlockByNumber(
|
|
|
|
|
m_blockSelection.firstBlockNumber());
|
|
|
|
|
const QTextBlock &lastBlock = m_document->document()->findBlockByNumber(
|
|
|
|
|
m_blockSelection.lastBlockNumber());
|
2014-02-26 11:16:38 +01:00
|
|
|
|
|
|
|
|
cursor.beginEditBlock();
|
|
|
|
|
for (;;) {
|
|
|
|
|
// get position of the selection
|
|
|
|
|
const QString &blockText = block.text();
|
|
|
|
|
const int startPos = block.position()
|
2014-07-25 16:23:50 +02:00
|
|
|
+ ts.positionAtColumn(blockText, m_blockSelection.firstVisualColumn());
|
2014-02-26 11:16:38 +01:00
|
|
|
const int endPos = block.position()
|
2014-07-25 16:23:50 +02:00
|
|
|
+ ts.positionAtColumn(blockText, m_blockSelection.lastVisualColumn());
|
2014-02-26 11:16:38 +01:00
|
|
|
|
|
|
|
|
// check if the selection is inside the text block
|
|
|
|
|
if (startPos < endPos) {
|
|
|
|
|
cursor.setPosition(startPos);
|
|
|
|
|
cursor.setPosition(endPos, QTextCursor::KeepAnchor);
|
2014-07-29 22:02:56 -07:00
|
|
|
const QString &transformedText = method(m_document->textAt(startPos, endPos - startPos));
|
2014-02-26 11:16:38 +01:00
|
|
|
if (transformedText != cursor.selectedText())
|
|
|
|
|
cursor.insertText(transformedText);
|
2012-08-17 16:35:48 +02:00
|
|
|
}
|
2014-02-26 11:16:38 +01:00
|
|
|
if (block == lastBlock)
|
2012-08-17 16:35:48 +02:00
|
|
|
break;
|
2014-02-26 11:16:38 +01:00
|
|
|
block = block.next();
|
2012-08-17 16:35:48 +02:00
|
|
|
}
|
2014-02-26 11:16:38 +01:00
|
|
|
cursor.endEditBlock();
|
2012-08-17 16:35:48 +02:00
|
|
|
|
|
|
|
|
// restore former block selection
|
2014-07-25 16:23:50 +02:00
|
|
|
enableBlockSelection(positionBlock, anchorColumn, anchorBlock, positionColumn);
|
2012-08-17 16:35:48 +02:00
|
|
|
}
|
|
|
|
|
|
2017-08-13 20:38:08 +03:00
|
|
|
void TextEditorWidgetPrivate::transformSelectedLines(ListTransformationMethod method)
|
|
|
|
|
{
|
|
|
|
|
if (!method || q->hasBlockSelection())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
QTextCursor cursor = q->textCursor();
|
|
|
|
|
if (!cursor.hasSelection())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
const bool downwardDirection = cursor.anchor() < cursor.position();
|
|
|
|
|
int startPosition = cursor.selectionStart();
|
|
|
|
|
int endPosition = cursor.selectionEnd();
|
|
|
|
|
|
|
|
|
|
cursor.setPosition(startPosition);
|
|
|
|
|
cursor.movePosition(QTextCursor::StartOfBlock);
|
|
|
|
|
startPosition = cursor.position();
|
|
|
|
|
|
|
|
|
|
cursor.setPosition(endPosition, QTextCursor::KeepAnchor);
|
|
|
|
|
if (cursor.positionInBlock() == 0)
|
|
|
|
|
cursor.movePosition(QTextCursor::PreviousBlock, QTextCursor::KeepAnchor);
|
|
|
|
|
cursor.movePosition(QTextCursor::EndOfBlock, QTextCursor::KeepAnchor);
|
|
|
|
|
endPosition = qMax(cursor.position(), endPosition);
|
|
|
|
|
|
|
|
|
|
const QString text = cursor.selectedText();
|
|
|
|
|
QStringList lines = text.split(QChar::ParagraphSeparator);
|
|
|
|
|
method(lines);
|
|
|
|
|
cursor.insertText(lines.join(QChar::ParagraphSeparator));
|
|
|
|
|
|
|
|
|
|
// (re)select the changed lines
|
|
|
|
|
// Note: this assumes the transformation did not change the length
|
|
|
|
|
cursor.setPosition(downwardDirection ? startPosition : endPosition);
|
|
|
|
|
cursor.setPosition(downwardDirection ? endPosition : startPosition, QTextCursor::KeepAnchor);
|
|
|
|
|
q->setTextCursor(cursor);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::inSnippetMode(bool *active)
|
2011-04-06 14:55:26 +02:00
|
|
|
{
|
|
|
|
|
*active = d->m_snippetOverlay->isVisible();
|
|
|
|
|
}
|
2011-04-15 16:19:23 +02:00
|
|
|
|
2015-04-16 15:05:53 +02:00
|
|
|
QTextBlock TextEditorWidget::blockForVisibleRow(int row) const
|
|
|
|
|
{
|
|
|
|
|
const int count = rowCount();
|
|
|
|
|
if (row < 0 && row >= count)
|
|
|
|
|
return QTextBlock();
|
|
|
|
|
|
|
|
|
|
QTextBlock block = firstVisibleBlock();
|
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
|
|
|
if (!block.isValid() || i == row)
|
|
|
|
|
return block;
|
|
|
|
|
|
|
|
|
|
while (block.isValid()) {
|
|
|
|
|
block = block.next();
|
|
|
|
|
if (block.isVisible())
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return QTextBlock();
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorWidget::invokeAssist(AssistKind kind, IAssistProvider *provider)
|
2011-04-15 16:19:23 +02:00
|
|
|
{
|
2014-11-25 16:37:31 +01:00
|
|
|
if (kind == QuickFix && d->m_snippetOverlay->isVisible()) {
|
|
|
|
|
d->m_snippetOverlay->setVisible(false);
|
|
|
|
|
d->m_snippetOverlay->mangle();
|
|
|
|
|
d->m_snippetOverlay->clear();
|
|
|
|
|
}
|
|
|
|
|
|
2012-03-16 14:43:15 +01:00
|
|
|
bool previousMode = overwriteMode();
|
|
|
|
|
setOverwriteMode(false);
|
2012-01-23 11:31:15 +01:00
|
|
|
ensureCursorVisible();
|
2014-08-20 15:31:39 +02:00
|
|
|
d->m_codeAssistant.invoke(kind, provider);
|
2012-03-16 14:43:15 +01:00
|
|
|
setOverwriteMode(previousMode);
|
2011-04-15 16:19:23 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
AssistInterface *TextEditorWidget::createAssistInterface(AssistKind kind,
|
2014-09-04 00:04:18 +02:00
|
|
|
AssistReason reason) const
|
2011-04-15 16:19:23 +02:00
|
|
|
{
|
|
|
|
|
Q_UNUSED(kind);
|
2014-12-21 21:54:30 +02:00
|
|
|
return new AssistInterface(document(), position(), d->m_document->filePath().toString(), reason);
|
2011-04-15 16:19:23 +02:00
|
|
|
}
|
2011-09-14 22:16:28 +02:00
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
QString TextEditorWidget::foldReplacementText(const QTextBlock &) const
|
2011-09-14 22:16:28 +02:00
|
|
|
{
|
|
|
|
|
return QLatin1String("...");
|
|
|
|
|
}
|
2013-05-24 14:23:07 +02:00
|
|
|
|
2014-07-23 19:10:38 +02:00
|
|
|
QByteArray BaseTextEditor::saveState() const
|
|
|
|
|
{
|
2014-08-20 13:56:57 +02:00
|
|
|
return editorWidget()->saveState();
|
2014-07-23 19:10:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool BaseTextEditor::restoreState(const QByteArray &state)
|
|
|
|
|
{
|
2014-08-20 13:56:57 +02:00
|
|
|
return editorWidget()->restoreState(state);
|
2014-07-23 19:10:38 +02:00
|
|
|
}
|
|
|
|
|
|
2014-07-25 14:10:41 +02:00
|
|
|
BaseTextEditor *BaseTextEditor::currentTextEditor()
|
|
|
|
|
{
|
2014-08-19 14:23:03 +02:00
|
|
|
return qobject_cast<BaseTextEditor *>(EditorManager::currentEditor());
|
2014-07-25 14:10:41 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
TextEditorWidget *BaseTextEditor::editorWidget() const
|
2014-07-26 12:45:48 +02:00
|
|
|
{
|
2014-09-26 11:37:54 +02:00
|
|
|
QTC_ASSERT(qobject_cast<TextEditorWidget *>(m_widget.data()), return 0);
|
|
|
|
|
return static_cast<TextEditorWidget *>(m_widget.data());
|
2014-07-26 12:45:48 +02:00
|
|
|
}
|
|
|
|
|
|
2014-08-27 11:57:32 +02:00
|
|
|
void BaseTextEditor::setTextCursor(const QTextCursor &cursor)
|
|
|
|
|
{
|
|
|
|
|
editorWidget()->setTextCursor(cursor);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QTextCursor BaseTextEditor::textCursor() const
|
|
|
|
|
{
|
|
|
|
|
return editorWidget()->textCursor();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QChar BaseTextEditor::characterAt(int pos) const
|
|
|
|
|
{
|
|
|
|
|
return textDocument()->characterAt(pos);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString BaseTextEditor::textAt(int from, int to) const
|
|
|
|
|
{
|
|
|
|
|
return textDocument()->textAt(from, to);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
QChar TextEditorWidget::characterAt(int pos) const
|
2014-09-03 08:35:58 +02:00
|
|
|
{
|
|
|
|
|
return textDocument()->characterAt(pos);
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
QString TextEditorWidget::textAt(int from, int to) const
|
2014-09-03 08:35:58 +02:00
|
|
|
{
|
|
|
|
|
return textDocument()->textAt(from, to);
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-03 09:18:57 +01:00
|
|
|
void TextEditorWidget::configureGenericHighlighter()
|
2014-07-30 10:04:01 +02:00
|
|
|
{
|
|
|
|
|
Highlighter *highlighter = new Highlighter();
|
2014-08-01 23:31:56 +02:00
|
|
|
highlighter->setTabSettings(textDocument()->tabSettings());
|
|
|
|
|
textDocument()->setSyntaxHighlighter(highlighter);
|
2014-07-30 10:04:01 +02:00
|
|
|
|
|
|
|
|
setCodeFoldingSupported(false);
|
|
|
|
|
|
2015-02-03 09:18:57 +01:00
|
|
|
const QString type = textDocument()->mimeType();
|
2017-03-02 12:07:11 +01:00
|
|
|
const MimeType mimeType = Utils::mimeTypeForName(type);
|
2015-02-04 09:32:46 +01:00
|
|
|
if (mimeType.isValid()) {
|
2014-07-30 10:04:01 +02:00
|
|
|
d->m_isMissingSyntaxDefinition = true;
|
|
|
|
|
|
2015-02-03 09:18:57 +01:00
|
|
|
QString definitionId;
|
2015-02-03 16:47:03 +01:00
|
|
|
setMimeTypeForHighlighter(highlighter, mimeType, textDocument()->filePath().toString(),
|
|
|
|
|
&definitionId);
|
2014-07-30 10:04:01 +02:00
|
|
|
|
|
|
|
|
if (!definitionId.isEmpty()) {
|
|
|
|
|
d->m_isMissingSyntaxDefinition = false;
|
|
|
|
|
const QSharedPointer<HighlightDefinition> &definition =
|
|
|
|
|
Manager::instance()->definition(definitionId);
|
|
|
|
|
if (!definition.isNull() && definition->isValid()) {
|
2014-09-01 16:16:44 +02:00
|
|
|
d->m_commentDefinition.isAfterWhiteSpaces = definition->isCommentAfterWhiteSpaces();
|
|
|
|
|
d->m_commentDefinition.singleLine = definition->singleLineComment();
|
|
|
|
|
d->m_commentDefinition.multiLineStart = definition->multiLineCommentStart();
|
|
|
|
|
d->m_commentDefinition.multiLineEnd = definition->multiLineCommentEnd();
|
2014-07-30 10:04:01 +02:00
|
|
|
|
|
|
|
|
setCodeFoldingSupported(true);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2015-03-02 09:18:35 +02:00
|
|
|
const QString fileName = textDocument()->filePath().toString();
|
2014-07-30 10:04:01 +02:00
|
|
|
if (TextEditorSettings::highlighterSettings().isIgnoredFilePattern(fileName))
|
|
|
|
|
d->m_isMissingSyntaxDefinition = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-01 23:31:56 +02:00
|
|
|
textDocument()->setFontSettings(TextEditorSettings::fontSettings());
|
2014-07-30 10:04:01 +02:00
|
|
|
|
2014-08-22 19:14:48 +02:00
|
|
|
updateEditorInfoBar(this);
|
2014-07-30 10:04:01 +02:00
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:05:53 +02:00
|
|
|
int TextEditorWidget::lineForVisibleRow(int row) const
|
|
|
|
|
{
|
|
|
|
|
QTextBlock block = blockForVisibleRow(row);
|
|
|
|
|
return block.isValid() ? block.blockNumber() : -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int TextEditorWidget::firstVisibleLine() const
|
|
|
|
|
{
|
|
|
|
|
return lineForVisibleRow(0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int TextEditorWidget::lastVisibleLine() const
|
|
|
|
|
{
|
|
|
|
|
QTextBlock block = blockForVisibleRow(rowCount() - 1);
|
|
|
|
|
if (!block.isValid())
|
|
|
|
|
block.previous();
|
|
|
|
|
return block.isValid() ? block.blockNumber() : -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int TextEditorWidget::centerVisibleLine() const
|
|
|
|
|
{
|
|
|
|
|
QTextBlock block = blockForVisibleRow(rowCount() / 2);
|
|
|
|
|
if (!block.isValid())
|
|
|
|
|
block.previous();
|
|
|
|
|
return block.isValid() ? block.blockNumber() : -1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
bool TextEditorWidget::isMissingSyntaxDefinition() const
|
2014-07-30 10:04:01 +02:00
|
|
|
{
|
|
|
|
|
return d->m_isMissingSyntaxDefinition;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-30 11:41:06 +02:00
|
|
|
// The remnants of PlainTextEditor.
|
2015-02-03 09:18:57 +01:00
|
|
|
void TextEditorWidget::setupGenericHighlighter()
|
2014-07-30 11:41:06 +02:00
|
|
|
{
|
|
|
|
|
setMarksVisible(true);
|
|
|
|
|
setLineSeparatorsAllowed(true);
|
|
|
|
|
|
2014-09-18 13:19:51 +02:00
|
|
|
connect(textDocument(), &IDocument::filePathChanged,
|
2014-09-26 11:37:54 +02:00
|
|
|
d, &TextEditorWidgetPrivate::reconfigure);
|
2014-08-22 19:14:48 +02:00
|
|
|
|
2015-02-03 16:47:03 +01:00
|
|
|
connect(Manager::instance(), &Manager::highlightingFilesRegistered,
|
2014-09-26 11:37:54 +02:00
|
|
|
d, &TextEditorWidgetPrivate::reconfigure);
|
2014-08-22 19:14:48 +02:00
|
|
|
|
|
|
|
|
updateEditorInfoBar(this);
|
2014-07-30 11:41:06 +02:00
|
|
|
}
|
|
|
|
|
|
2014-10-15 13:27:22 +02:00
|
|
|
//
|
|
|
|
|
// TextEditorLinkLabel
|
|
|
|
|
//
|
|
|
|
|
TextEditorLinkLabel::TextEditorLinkLabel(QWidget *parent)
|
|
|
|
|
: QLabel(parent)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TextEditorLinkLabel::setLink(TextEditorWidget::Link link)
|
|
|
|
|
{
|
|
|
|
|
m_link = link;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TextEditorWidget::Link TextEditorLinkLabel::link() const
|
|
|
|
|
{
|
|
|
|
|
return m_link;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TextEditorLinkLabel::mousePressEvent(QMouseEvent *event)
|
|
|
|
|
{
|
|
|
|
|
if (event->button() == Qt::LeftButton)
|
|
|
|
|
m_dragStartPosition = event->pos();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TextEditorLinkLabel::mouseMoveEvent(QMouseEvent *event)
|
|
|
|
|
{
|
|
|
|
|
if (!(event->buttons() & Qt::LeftButton))
|
|
|
|
|
return;
|
|
|
|
|
if ((event->pos() - m_dragStartPosition).manhattanLength() < QApplication::startDragDistance())
|
|
|
|
|
return;
|
|
|
|
|
|
2015-06-04 12:35:59 +02:00
|
|
|
auto data = new DropMimeData;
|
2014-10-15 13:27:22 +02:00
|
|
|
data->addFile(m_link.targetFileName, m_link.targetLine, m_link.targetColumn);
|
|
|
|
|
auto drag = new QDrag(this);
|
|
|
|
|
drag->setMimeData(data);
|
|
|
|
|
drag->exec(Qt::MoveAction);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TextEditorLinkLabel::mouseReleaseEvent(QMouseEvent *event)
|
|
|
|
|
{
|
|
|
|
|
Q_UNUSED(event)
|
|
|
|
|
if (!m_link.hasValidTarget())
|
|
|
|
|
return;
|
|
|
|
|
|
2015-02-03 23:46:35 +02:00
|
|
|
EditorManager::openEditorAt(m_link.targetFileName, m_link.targetLine, m_link.targetColumn);
|
2014-10-15 13:27:22 +02:00
|
|
|
}
|
|
|
|
|
|
2014-08-22 12:14:06 +02:00
|
|
|
//
|
|
|
|
|
// BaseTextEditorFactory
|
|
|
|
|
//
|
|
|
|
|
|
2014-10-15 00:36:39 +02:00
|
|
|
namespace Internal {
|
|
|
|
|
|
|
|
|
|
class TextEditorFactoryPrivate
|
2014-08-22 12:14:06 +02:00
|
|
|
{
|
2014-10-15 00:36:39 +02:00
|
|
|
public:
|
|
|
|
|
TextEditorFactoryPrivate(TextEditorFactory *parent) :
|
|
|
|
|
q(parent),
|
|
|
|
|
m_widgetCreator([]() { return new TextEditorWidget; }),
|
2017-07-25 12:32:45 +02:00
|
|
|
m_editorCreator([]() { return new BaseTextEditor; })
|
2014-10-15 00:36:39 +02:00
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
BaseTextEditor *duplicateTextEditor(BaseTextEditor *other)
|
|
|
|
|
{
|
|
|
|
|
BaseTextEditor *editor = createEditorHelper(other->editorWidget()->textDocumentPtr());
|
|
|
|
|
editor->editorWidget()->finalizeInitializationAfterDuplication(other->editorWidget());
|
|
|
|
|
return editor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseTextEditor *createEditorHelper(const TextDocumentPtr &doc);
|
|
|
|
|
|
|
|
|
|
TextEditorFactory *q;
|
|
|
|
|
TextEditorFactory::DocumentCreator m_documentCreator;
|
|
|
|
|
TextEditorFactory::EditorWidgetCreator m_widgetCreator;
|
|
|
|
|
TextEditorFactory::EditorCreator m_editorCreator;
|
|
|
|
|
TextEditorFactory::AutoCompleterCreator m_autoCompleterCreator;
|
|
|
|
|
TextEditorFactory::IndenterCreator m_indenterCreator;
|
|
|
|
|
TextEditorFactory::SyntaxHighLighterCreator m_syntaxHighlighterCreator;
|
2017-04-24 16:01:14 +02:00
|
|
|
CommentDefinition m_commentDefinition;
|
2014-10-15 00:36:39 +02:00
|
|
|
QList<BaseHoverHandler *> m_hoverHandlers; // owned
|
2017-07-25 12:32:45 +02:00
|
|
|
CompletionAssistProvider * m_completionAssistProvider = nullptr; // owned
|
|
|
|
|
bool m_useGenericHighlighter = false;
|
|
|
|
|
bool m_duplicatedSupported = true;
|
|
|
|
|
bool m_codeFoldingSupported = false;
|
|
|
|
|
bool m_paranthesesMatchinEnabled = false;
|
|
|
|
|
bool m_marksVisible = false;
|
2014-10-15 00:36:39 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
} /// namespace Internal
|
|
|
|
|
|
|
|
|
|
TextEditorFactory::TextEditorFactory(QObject *parent)
|
|
|
|
|
: IEditorFactory(parent), d(new TextEditorFactoryPrivate(this))
|
|
|
|
|
{}
|
2014-08-22 12:14:06 +02:00
|
|
|
|
2014-09-30 13:08:05 +02:00
|
|
|
TextEditorFactory::~TextEditorFactory()
|
|
|
|
|
{
|
2014-10-15 00:36:39 +02:00
|
|
|
qDeleteAll(d->m_hoverHandlers);
|
|
|
|
|
delete d->m_completionAssistProvider;
|
|
|
|
|
delete d;
|
2014-09-30 13:08:05 +02:00
|
|
|
}
|
|
|
|
|
|
2016-05-31 10:59:45 +02:00
|
|
|
void TextEditorFactory::setDocumentCreator(const DocumentCreator &creator)
|
2014-08-22 12:14:06 +02:00
|
|
|
{
|
2016-05-31 10:59:45 +02:00
|
|
|
d->m_documentCreator = creator;
|
2014-08-22 12:14:06 +02:00
|
|
|
}
|
|
|
|
|
|
2016-05-31 10:59:45 +02:00
|
|
|
void TextEditorFactory::setEditorWidgetCreator(const EditorWidgetCreator &creator)
|
2014-08-22 12:14:06 +02:00
|
|
|
{
|
2016-05-31 10:59:45 +02:00
|
|
|
d->m_widgetCreator = creator;
|
2014-08-22 12:14:06 +02:00
|
|
|
}
|
|
|
|
|
|
2016-05-31 10:59:45 +02:00
|
|
|
void TextEditorFactory::setEditorCreator(const EditorCreator &creator)
|
2014-08-22 12:14:06 +02:00
|
|
|
{
|
2016-05-31 10:59:45 +02:00
|
|
|
d->m_editorCreator = creator;
|
2014-08-22 12:14:06 +02:00
|
|
|
}
|
|
|
|
|
|
2016-05-31 10:59:45 +02:00
|
|
|
void TextEditorFactory::setIndenterCreator(const IndenterCreator &creator)
|
2014-08-22 12:14:06 +02:00
|
|
|
{
|
2016-05-31 10:59:45 +02:00
|
|
|
d->m_indenterCreator = creator;
|
2014-08-22 12:14:06 +02:00
|
|
|
}
|
|
|
|
|
|
2016-05-31 10:59:45 +02:00
|
|
|
void TextEditorFactory::setSyntaxHighlighterCreator(const SyntaxHighLighterCreator &creator)
|
2014-08-22 12:14:06 +02:00
|
|
|
{
|
2016-05-31 10:59:45 +02:00
|
|
|
d->m_syntaxHighlighterCreator = creator;
|
2014-08-22 12:14:06 +02:00
|
|
|
}
|
|
|
|
|
|
2015-02-03 09:18:57 +01:00
|
|
|
void TextEditorFactory::setUseGenericHighlighter(bool enabled)
|
2014-12-05 15:47:49 +01:00
|
|
|
{
|
2015-02-03 09:18:57 +01:00
|
|
|
d->m_useGenericHighlighter = enabled;
|
2014-12-05 15:47:49 +01:00
|
|
|
}
|
|
|
|
|
|
2016-05-31 10:59:45 +02:00
|
|
|
void TextEditorFactory::setAutoCompleterCreator(const AutoCompleterCreator &creator)
|
2014-08-22 12:49:02 +02:00
|
|
|
{
|
2016-05-31 10:59:45 +02:00
|
|
|
d->m_autoCompleterCreator = creator;
|
2014-08-22 12:49:02 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorFactory::setEditorActionHandlers(Id contextId, uint optionalActions)
|
2014-08-22 12:14:06 +02:00
|
|
|
{
|
2017-05-08 16:06:35 +02:00
|
|
|
new TextEditorActionHandler(this, id(), contextId, optionalActions);
|
2014-08-22 12:14:06 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorFactory::setEditorActionHandlers(uint optionalActions)
|
2014-08-22 12:14:06 +02:00
|
|
|
{
|
2017-05-08 16:06:35 +02:00
|
|
|
new TextEditorActionHandler(this, id(), id(), optionalActions);
|
2014-08-22 12:14:06 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-30 13:08:05 +02:00
|
|
|
void TextEditorFactory::addHoverHandler(BaseHoverHandler *handler)
|
|
|
|
|
{
|
2014-10-15 00:36:39 +02:00
|
|
|
d->m_hoverHandlers.append(handler);
|
2014-09-30 13:08:05 +02:00
|
|
|
}
|
|
|
|
|
|
2014-10-01 22:39:47 +02:00
|
|
|
void TextEditorFactory::setCompletionAssistProvider(CompletionAssistProvider *provider)
|
|
|
|
|
{
|
2014-10-15 00:36:39 +02:00
|
|
|
d->m_completionAssistProvider = provider;
|
2014-10-01 22:39:47 +02:00
|
|
|
}
|
|
|
|
|
|
2017-04-24 16:01:14 +02:00
|
|
|
void TextEditorFactory::setCommentDefinition(CommentDefinition definition)
|
2014-09-01 16:16:44 +02:00
|
|
|
{
|
2017-04-24 16:01:14 +02:00
|
|
|
d->m_commentDefinition = definition;
|
2014-09-01 16:16:44 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
void TextEditorFactory::setDuplicatedSupported(bool on)
|
2014-09-03 11:24:38 +02:00
|
|
|
{
|
2014-10-15 00:36:39 +02:00
|
|
|
d->m_duplicatedSupported = on;
|
2014-09-03 11:24:38 +02:00
|
|
|
}
|
|
|
|
|
|
2014-10-15 00:36:39 +02:00
|
|
|
void TextEditorFactory::setMarksVisible(bool on)
|
2014-08-22 12:14:06 +02:00
|
|
|
{
|
2014-10-15 00:36:39 +02:00
|
|
|
d->m_marksVisible = on;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TextEditorFactory::setCodeFoldingSupported(bool on)
|
|
|
|
|
{
|
|
|
|
|
d->m_codeFoldingSupported = on;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TextEditorFactory::setParenthesesMatchingEnabled(bool on)
|
|
|
|
|
{
|
|
|
|
|
d->m_paranthesesMatchinEnabled = on;
|
2014-08-22 12:14:06 +02:00
|
|
|
}
|
|
|
|
|
|
2014-09-26 11:37:54 +02:00
|
|
|
IEditor *TextEditorFactory::createEditor()
|
2014-08-22 12:14:06 +02:00
|
|
|
{
|
2017-07-26 10:53:37 +02:00
|
|
|
static KeywordsCompletionAssistProvider basicSnippetProvider;
|
2014-10-15 00:36:39 +02:00
|
|
|
TextDocumentPtr doc(d->m_documentCreator());
|
2014-08-22 12:14:06 +02:00
|
|
|
|
2014-10-15 00:36:39 +02:00
|
|
|
if (d->m_indenterCreator)
|
|
|
|
|
doc->setIndenter(d->m_indenterCreator());
|
2014-08-22 12:14:06 +02:00
|
|
|
|
2014-10-15 00:36:39 +02:00
|
|
|
if (d->m_syntaxHighlighterCreator)
|
|
|
|
|
doc->setSyntaxHighlighter(d->m_syntaxHighlighterCreator());
|
2014-10-01 22:39:47 +02:00
|
|
|
|
2017-07-26 10:53:37 +02:00
|
|
|
doc->setCompletionAssistProvider(d->m_completionAssistProvider ? d->m_completionAssistProvider
|
|
|
|
|
: &basicSnippetProvider);
|
2014-08-22 12:14:06 +02:00
|
|
|
|
2014-10-15 00:36:39 +02:00
|
|
|
return d->createEditorHelper(doc);
|
2014-08-22 12:14:06 +02:00
|
|
|
}
|
|
|
|
|
|
2014-10-15 00:36:39 +02:00
|
|
|
BaseTextEditor *TextEditorFactoryPrivate::createEditorHelper(const TextDocumentPtr &document)
|
2014-08-22 12:14:06 +02:00
|
|
|
{
|
2014-09-26 11:37:54 +02:00
|
|
|
TextEditorWidget *widget = m_widgetCreator();
|
2014-10-15 00:36:39 +02:00
|
|
|
widget->setMarksVisible(m_marksVisible);
|
|
|
|
|
widget->setParenthesesMatchingEnabled(m_paranthesesMatchinEnabled);
|
|
|
|
|
widget->setCodeFoldingSupported(m_codeFoldingSupported);
|
|
|
|
|
|
2014-08-22 12:14:06 +02:00
|
|
|
BaseTextEditor *editor = m_editorCreator();
|
2014-09-03 11:24:38 +02:00
|
|
|
editor->setDuplicateSupported(m_duplicatedSupported);
|
2014-10-15 00:36:39 +02:00
|
|
|
editor->addContext(q->id());
|
2014-08-22 12:14:06 +02:00
|
|
|
editor->d->m_origin = this;
|
|
|
|
|
|
|
|
|
|
editor->m_widget = widget;
|
2014-09-10 17:07:32 +02:00
|
|
|
|
|
|
|
|
// Needs to go before setTextDocument as this copies the current settings.
|
|
|
|
|
if (m_autoCompleterCreator)
|
|
|
|
|
widget->setAutoCompleter(m_autoCompleterCreator());
|
|
|
|
|
|
2014-08-22 12:14:06 +02:00
|
|
|
widget->setTextDocument(document);
|
2017-07-17 12:41:37 +02:00
|
|
|
widget->autoCompleter()->setTabSettings(document->tabSettings());
|
2014-09-30 13:08:05 +02:00
|
|
|
widget->d->m_hoverHandlers = m_hoverHandlers;
|
2014-08-22 12:14:06 +02:00
|
|
|
|
2014-09-03 22:45:33 +02:00
|
|
|
widget->d->m_codeAssistant.configure(widget);
|
2017-04-24 16:01:14 +02:00
|
|
|
widget->d->m_commentDefinition = m_commentDefinition;
|
2014-08-22 12:14:06 +02:00
|
|
|
|
2014-10-15 00:36:39 +02:00
|
|
|
QObject::connect(widget, &TextEditorWidget::activateEditor,
|
2015-02-03 23:46:35 +02:00
|
|
|
[editor]() { EditorManager::activateEditor(editor); });
|
2014-09-02 09:32:25 +02:00
|
|
|
|
2015-02-03 09:18:57 +01:00
|
|
|
if (m_useGenericHighlighter)
|
|
|
|
|
widget->setupGenericHighlighter();
|
2014-08-22 16:38:38 +02:00
|
|
|
widget->finalizeInitialization();
|
|
|
|
|
editor->finalizeInitialization();
|
|
|
|
|
|
2014-10-15 00:36:39 +02:00
|
|
|
QObject::connect(widget->d->m_cursorPositionLabel, &LineColumnLabel::clicked, [editor] {
|
2014-09-02 09:01:31 +02:00
|
|
|
EditorManager::activateEditor(editor, EditorManager::IgnoreNavigationHistory);
|
2015-02-03 23:46:35 +02:00
|
|
|
if (Command *cmd = ActionManager::command(Core::Constants::GOTO)) {
|
2014-09-02 09:01:31 +02:00
|
|
|
if (QAction *act = cmd->action())
|
|
|
|
|
act->trigger();
|
|
|
|
|
}
|
|
|
|
|
});
|
2014-08-22 12:14:06 +02:00
|
|
|
return editor;
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-15 00:36:39 +02:00
|
|
|
IEditor *BaseTextEditor::duplicate()
|
|
|
|
|
{
|
|
|
|
|
// Use new standard setup if that's available.
|
|
|
|
|
if (d->m_origin)
|
|
|
|
|
return d->m_origin->duplicateTextEditor(this);
|
|
|
|
|
|
|
|
|
|
// If neither is sufficient, you need to implement 'YourEditor::duplicate'.
|
|
|
|
|
QTC_CHECK(false);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-05-24 14:23:07 +02:00
|
|
|
} // namespace TextEditor
|
2014-07-23 19:10:38 +02:00
|
|
|
|
2017-07-04 18:10:07 +02:00
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
|
2017-06-20 08:28:10 +02:00
|
|
|
uint qHash(const QColor &color)
|
|
|
|
|
{
|
|
|
|
|
return color.rgba();
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-04 18:10:07 +02:00
|
|
|
QT_END_NAMESPACE
|
|
|
|
|
|
2014-09-26 09:14:03 +02:00
|
|
|
#include "texteditor.moc"
|