subsurface/qt-ui/tagwidget.cpp
Dirk Hohndel f262ed69cc Make the tag widget act more sanely when pressing Tab
When dealing with autocompletion, tag usually means "take this, move on".
In the tag widget the tab was added to the tag itself (and then stripped
when the input line was processed). Not exactly useful.

This feels a bit "hackish", but it seems to get the job done.

Signed-off-by: Dirk Hohndel <dirk@hohndel.org>
2013-11-07 13:40:57 +09:00

175 lines
3.9 KiB
C++

#include "tagwidget.h"
#include <QPair>
#include <QDebug>
#include <QAbstractItemView>
TagWidget::TagWidget(QWidget *parent) : GroupedLineEdit(parent), m_completer(NULL)
{
connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(reparse()));
connect(this, SIGNAL(textChanged()), this, SLOT(reparse()));
addColor(QColor(0x00, 0xAE, 0xFF));
addColor(QColor(0x00, 0x78, 0xB0));
}
void TagWidget::setCompleter(QCompleter *completer)
{
m_completer = completer;
m_completer->setWidget(this);
connect(m_completer, SIGNAL(activated(QString)), this, SLOT(completionSelected(QString)));
connect(m_completer, SIGNAL(highlighted(QString)), this, SLOT(completionSelected(QString)));
}
QPair<int,int> TagWidget::getCursorTagPosition() {
int i = 0, start = 0, end = 0;
/* Parse string near cursor */
i = cursorPosition();
while (--i > 0) {
if (text().at(i) == ',') {
if (i > 0 && text().at(i-1) != '\\') {
i++;
break;
}
}
}
start = i;
while (++i < text().length()) {
if (text().at(i) == ',') {
if (i > 0 && text().at(i-1) != '\\')
break;
}
}
end = i;
if (start < 0 || end < 0) {
start = 0;
end = 0;
}
return QPair<int,int>(start,end);
}
enum ParseState {FINDSTART, FINDEND};
void TagWidget::highlight() {
int i = 0, start = 0, end = 0;
ParseState state = FINDEND;
removeAllBlocks();
while(i < text().length()) {
if (text().at(i) == ',') {
if (state == FINDSTART) {
/* Detect empty tags */
} else if (state == FINDEND) {
/* Found end of tag */
if (i > 1) {
if(text().at(i-1) != '\\') {
addBlock(start, end);
state = FINDSTART;
}
} else {
state = FINDSTART;
}
}
} else if (text().at(i) == ' ') {
/* Handled */
} else {
/* Found start of tag */
if (state == FINDSTART) {
state = FINDEND;
start = i;
} else if (state == FINDEND) {
end = i;
}
}
i++;
}
if (state == FINDEND) {
if (end < start)
end = text().length()-1;
if (text().length() > 0)
addBlock(start, end);
}
}
void TagWidget::reparse()
{
highlight();
QPair<int,int> pos = getCursorTagPosition();
QString currentText;
if (pos.first >= 0 && pos.second > 0)
currentText = text().mid(pos.first, pos.second-pos.first).trimmed();
else
currentText = "";
if (m_completer) {
m_completer->setCompletionPrefix(currentText);
if (m_completer->completionCount() == 1) {
if (m_completer->currentCompletion() == currentText) {
QAbstractItemView *popup = m_completer->popup();
if (popup)
popup->hide();
}
else
m_completer->complete();
} else {
m_completer->complete();
}
}
}
void TagWidget::completionSelected(QString completion) {
QPair <int,int> pos;
pos = getCursorTagPosition();
if (pos.first >= 0 && pos.second > 0) {
setText(text().remove(pos.first, pos.second-pos.first).insert(pos.first, completion));
setCursorPosition(pos.first+completion.length());
}
else {
setText(completion.append(", "));
setCursorPosition(text().length());
}
}
void TagWidget::setCursorPosition(int position) {
blockSignals(true);
GroupedLineEdit::setCursorPosition(position);
blockSignals(false);
}
void TagWidget::setText(QString text) {
blockSignals(true);
GroupedLineEdit::setText(text);
blockSignals(false);
highlight();
}
void TagWidget::clear() {
blockSignals(true);
GroupedLineEdit::clear();
blockSignals(false);
}
void TagWidget::keyPressEvent(QKeyEvent *e) {
switch (e->key()) {
case Qt::Key_Return:
case Qt::Key_Enter:
case Qt::Key_Tab:
/*
* Fake the QLineEdit behaviour by simply
* closing the QAbstractViewitem
*/
if (m_completer) {
QAbstractItemView *popup = m_completer->popup();
if (popup)
popup->hide();
}
}
if (e->key() == Qt::Key_Tab) { // let's pretend this is a comma instead
QKeyEvent *fakeEvent = new QKeyEvent(e->type(), Qt::Key_Comma, e->modifiers(), QString(","));
qDebug() << "sending comma instead";
GroupedLineEdit::keyPressEvent(fakeEvent);
free(fakeEvent);
} else {
GroupedLineEdit::keyPressEvent(e);
}
}