2 Copyright 2013-2015 Mats Sjöberg
4 This file is part of the Pumpa programme.
6 Pumpa is free software: you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 Pumpa is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14 License for more details.
16 You should have received a copy of the GNU General Public License
17 along with Pumpa. If not, see <http://www.gnu.org/licenses/>.
20 #include "messageedit.h"
21 #include "pumpa_defines.h"
26 #include <QAbstractItemView>
28 //------------------------------------------------------------------------------
30 MessageEdit::MessageEdit(const PumpaSettings
* s
, QWidget
* parent
) :
36 setAcceptRichText(false);
39 m_checker
= new QASpell(this);
42 m_highlighter
= new FancyHighlighter(document(), m_checker
);
44 m_completer
= new QCompleter(this);
45 m_completer
->setWidget(this);
47 m_completer
->setCaseSensitivity(Qt::CaseInsensitive
);
48 m_completer
->setCompletionMode(QCompleter::PopupCompletion
);
49 m_completer
->setModelSorting(QCompleter::UnsortedModel
);
50 m_completer
->setMaxVisibleItems(10);
52 m_model
= new QStringListModel(this);
53 m_completer
->setModel(m_model
);
55 connect(m_completer
, SIGNAL(activated(QString
)),
56 this, SLOT(insertCompletion(QString
)));
59 //------------------------------------------------------------------------------
61 void MessageEdit::setCompletions(const completion_t
* completions
) {
62 m_completions
= completions
;
63 m_model
->setStringList(m_completions
->keys());
66 //------------------------------------------------------------------------------
68 void MessageEdit::hideCompletion() {
70 m_completer
->popup()->hide();
73 //------------------------------------------------------------------------------
75 // completion code partially from here:
76 // http://qt-project.org/forums/viewthread/5376
78 void MessageEdit::keyPressEvent(QKeyEvent
* event
) {
79 int key
= event
->key();
80 int mods
= event
->modifiers();
82 QAbstractItemView
* popup
= m_completer
->popup();
84 if (popup
->isVisible()) {
96 if (key
== Qt::Key_Return
&& (mods
& Qt::ControlModifier
)) {
101 QTextEdit::keyPressEvent(event
);
103 const QString completionPrefix
= wordAtCursor();
105 if (completionPrefix
!= m_completer
->completionPrefix()) {
106 m_completer
->setCompletionPrefix(completionPrefix
);
107 QModelIndex idx
= m_completer
->completionModel()->index(0, 0);
108 popup
->setCurrentIndex(idx
);
111 if (!event
->text().isEmpty() && completionPrefix
.length() >= 2) {
112 QRect r
= cursorRect();
113 r
.setWidth(popup
->sizeHintForColumn(0) +
114 popup
->verticalScrollBar()->sizeHint().width());
115 r
.setHeight(popup
->sizeHintForRow(0));
116 m_model
->setStringList(m_completions
->keys());
117 m_completer
->complete(r
);
123 //------------------------------------------------------------------------------
125 void MessageEdit::insertCompletion(QString completion
) {
126 if (m_completer
->widget() != this)
129 // qDebug() << "insertCompletion" << completion;
133 QTextCursor tc
= textCursor();
134 tc
.select(QTextCursor::WordUnderCursor
);
136 tc
.removeSelectedText();
137 tc
.deletePreviousChar();
139 QASActor
* actor
= m_completions
->value(completion
);
140 QString newText
= m_s
->useMarkdown() ?
141 QString("[@%1](%2)").arg(actor
->displayNameOrWebFinger()).arg(actor
->url()):
142 QString("@%1").arg(actor
->displayNameOrWebFinger());
143 tc
.insertText(newText
+ " ");
146 emit
addRecipient(actor
);
149 //------------------------------------------------------------------------------
151 QString
MessageEdit::wordAtCursor() const {
152 QTextCursor tc
= textCursor();
153 tc
.select(QTextCursor::WordUnderCursor
);
154 if (tc
.document()->characterAt(tc
.selectionStart()-1) == '@')
155 return tc
.selectedText();
159 //------------------------------------------------------------------------------
161 void MessageEdit::focusInEvent(QFocusEvent
*event
) {
163 m_completer
->setWidget(this);
164 QTextEdit::focusInEvent(event
);
167 //------------------------------------------------------------------------------
169 void MessageEdit::contextMenuEvent(QContextMenuEvent
* event
) {
170 QMenu
* menu
= createStandardContextMenu();
173 m_contextCursor
= cursorForPosition(event
->pos());
174 m_contextCursor
.select(QTextCursor::WordUnderCursor
);
176 QTextDocument
* doc
= m_contextCursor
.document();
178 QRegExp
rx("[\\w']");
179 int pos
= m_contextCursor
.selectionEnd();
180 while (rx
.exactMatch(doc
->characterAt(pos
))) {
182 m_contextCursor
.setPosition(pos
, QTextCursor::KeepAnchor
);
185 QString word
= m_contextCursor
.selectedText();
186 if (word
.endsWith("'"))
187 word
.remove(word
.size()-1, 1);
189 if (!word
.isEmpty() && m_checker
&& !m_checker
->checkWord(word
)) {
190 QStringList suggestions
= m_checker
->suggestions(word
);
191 if (!suggestions
.empty()) {
192 QMenu
* m
= menu
->addMenu(tr("Spelling suggestions..."));
193 m_sMapper
= new QSignalMapper(this);
195 connect(m_sMapper
, SIGNAL(mapped(QString
)),
196 this, SLOT(replaceSuggestion(QString
)));
198 for (int i
=0; i
<suggestions
.size() && i
<MAX_SUGGESTIONS
; ++i
) {
199 QString sWord
= suggestions
[i
];
200 QAction
* act
= m
->addAction(sWord
);
201 connect(act
, SIGNAL(triggered()), m_sMapper
, SLOT(map()));
202 m_sMapper
->setMapping(act
, sWord
);
208 menu
->exec(event
->globalPos());
212 //------------------------------------------------------------------------------
214 void MessageEdit::replaceSuggestion(const QString
& word
) {
215 if (m_contextCursor
.isNull() || word
.isEmpty())
218 m_contextCursor
.removeSelectedText();
219 m_contextCursor
.insertText(word
);
221 m_contextCursor
= QTextCursor();