No longer honours following status in JSON, instead relies solely on following list.
[larjonas-pumpa.git] / src / messageedit.cpp
blob43c5078eb82d4969df00de4a927f8f7e57f43737
1 /*
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"
23 #include <QMenu>
24 #include <QDebug>
25 #include <QScrollBar>
26 #include <QAbstractItemView>
28 //------------------------------------------------------------------------------
30 MessageEdit::MessageEdit(const PumpaSettings* s, QWidget* parent) :
31 QTextEdit(parent),
32 m_completions(NULL),
33 m_checker(NULL),
34 m_s(s)
36 setAcceptRichText(false);
38 #ifdef USE_ASPELL
39 m_checker = new QASpell(this);
40 #endif
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() {
69 if (m_completer)
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()) {
85 switch (key) {
86 case Qt::Key_Enter:
87 case Qt::Key_Return:
88 case Qt::Key_Tab:
89 case Qt::Key_Escape:
90 hideCompletion();
91 event->ignore();
92 return;
96 if (key == Qt::Key_Return && (mods & Qt::ControlModifier)) {
97 emit ready();
98 return;
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);
118 } else {
119 hideCompletion();
123 //------------------------------------------------------------------------------
125 void MessageEdit::insertCompletion(QString completion) {
126 if (m_completer->widget() != this)
127 return;
129 // qDebug() << "insertCompletion" << completion;
131 hideCompletion();
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 + " ");
144 setTextCursor(tc);
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();
156 return "";
159 //------------------------------------------------------------------------------
161 void MessageEdit::focusInEvent(QFocusEvent *event) {
162 if (m_completer)
163 m_completer->setWidget(this);
164 QTextEdit::focusInEvent(event);
167 //------------------------------------------------------------------------------
169 void MessageEdit::contextMenuEvent(QContextMenuEvent* event) {
170 QMenu* menu = createStandardContextMenu();
172 #ifdef USE_ASPELL
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))) {
181 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);
206 #endif
208 menu->exec(event->globalPos());
209 delete menu;
212 //------------------------------------------------------------------------------
214 void MessageEdit::replaceSuggestion(const QString& word) {
215 if (m_contextCursor.isNull() || word.isEmpty())
216 return;
218 m_contextCursor.removeSelectedText();
219 m_contextCursor.insertText(word);
221 m_contextCursor = QTextCursor();