203 lines
5.1 KiB
C++
203 lines
5.1 KiB
C++
#include "gsceditor.h"
|
|
|
|
#include <QAbstractItemView>
|
|
#include <QKeyEvent>
|
|
#include <QScrollbar>
|
|
#include <QFile>
|
|
#include <QFileDialog>
|
|
|
|
#define TAB_STOP 4
|
|
|
|
GSCEditor::GSCEditor(QWidget *aParent)
|
|
: QTextEdit(aParent)
|
|
, mSaved(true)
|
|
, mCompleter()
|
|
{
|
|
QFont font;
|
|
font.setFamily("Courier");
|
|
font.setFixedPitch(true);
|
|
font.setPointSize(10);
|
|
font.setStyleHint(QFont::Monospace);
|
|
|
|
setFont(font);
|
|
|
|
setTabStopDistance(TAB_STOP * fontMetrics().horizontalAdvance(' '));
|
|
|
|
connect(this, &QTextEdit::textChanged, this, &GSCEditor::checkPlainTextChanged);
|
|
}
|
|
|
|
GSCEditor::~GSCEditor()
|
|
{
|
|
delete mCompleter;
|
|
}
|
|
|
|
void GSCEditor::checkPlainTextChanged()
|
|
{
|
|
const QString plainText = toPlainText();
|
|
if (mLastText != plainText)
|
|
{
|
|
mSaved = false;
|
|
}
|
|
else
|
|
{
|
|
mSaved = true;
|
|
}
|
|
emit saveStatusChanged(mSaved);
|
|
}
|
|
|
|
void GSCEditor::setCompleter(QCompleter *aCompleter)
|
|
{
|
|
if (aCompleter)
|
|
{
|
|
aCompleter->disconnect(this);
|
|
}
|
|
|
|
mCompleter = aCompleter;
|
|
|
|
if (!mCompleter) { return; }
|
|
|
|
mCompleter->setWidget(this);
|
|
mCompleter->setCompletionMode(QCompleter::PopupCompletion);
|
|
mCompleter->setCaseSensitivity(Qt::CaseInsensitive);
|
|
|
|
// TODO: Wtf is a QOverload
|
|
connect(mCompleter, QOverload<const QString&>::of(&QCompleter::activated), this, &GSCEditor::insertCompletion);
|
|
}
|
|
|
|
QCompleter *GSCEditor::completer() const
|
|
{
|
|
return mCompleter;
|
|
}
|
|
|
|
void GSCEditor::setSaveableText(const QString &aText)
|
|
{
|
|
setPlainText(aText);
|
|
mLastText = toPlainText();
|
|
}
|
|
|
|
bool GSCEditor::saved() const
|
|
{
|
|
return mSaved;
|
|
}
|
|
|
|
void GSCEditor::save()
|
|
{
|
|
const QString filePath = property("Path").toString();
|
|
QFile saveFile(filePath);
|
|
if (!saveFile.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text))
|
|
{
|
|
qDebug() << "ERROR: Failed to open file " << filePath;
|
|
return;
|
|
}
|
|
saveFile.write(toPlainText().toUtf8());
|
|
|
|
mLastText = toPlainText();
|
|
mSaved = true;
|
|
emit saveStatusChanged(mSaved);
|
|
}
|
|
|
|
void GSCEditor::saveAs()
|
|
{
|
|
QString filePath = property("Path").toString();
|
|
|
|
QFileDialog saveAsDialog(this);
|
|
saveAsDialog.setFileMode(QFileDialog::AnyFile);
|
|
saveAsDialog.setWindowTitle("Open Folder");
|
|
saveAsDialog.setDirectory(QDir(filePath).path());
|
|
|
|
if (saveAsDialog.exec() == QDialog::Rejected) { return; }
|
|
|
|
filePath = saveAsDialog.selectedFiles().first();
|
|
|
|
QFile saveFile(filePath);
|
|
if (!saveFile.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text))
|
|
{
|
|
qDebug() << "ERROR: Failed to open file " << filePath;
|
|
return;
|
|
}
|
|
saveFile.write(toPlainText().toUtf8());
|
|
|
|
mLastText = toPlainText();
|
|
mSaved = true;
|
|
emit saveStatusChanged(mSaved);
|
|
}
|
|
|
|
void GSCEditor::keyPressEvent(QKeyEvent *aEvent)
|
|
{
|
|
if (mCompleter && mCompleter->popup()->isVisible())
|
|
{
|
|
switch (aEvent->key())
|
|
{
|
|
case Qt::Key_Enter:
|
|
case Qt::Key_Return:
|
|
case Qt::Key_Escape:
|
|
case Qt::Key_Tab:
|
|
case Qt::Key_Backtab:
|
|
aEvent->ignore();
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Accept CTRL+SPACE to auto-complete
|
|
const bool isShortcut = (aEvent->modifiers().testFlag(Qt::ControlModifier)
|
|
&& aEvent->key() == Qt::Key_Space);
|
|
if (!mCompleter || !isShortcut)
|
|
{
|
|
QTextEdit::keyPressEvent(aEvent);
|
|
}
|
|
|
|
const bool ctrlOrShift = aEvent->modifiers().testFlag(Qt::ControlModifier)
|
|
|| aEvent->modifiers().testFlag(Qt::ShiftModifier);
|
|
if (!mCompleter || (ctrlOrShift && aEvent->text().isEmpty())) { return; }
|
|
|
|
static QString eow("~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-=");
|
|
const bool hasModifier = (aEvent->modifiers() != Qt::NoModifier) && !ctrlOrShift;
|
|
QString completionPrefix = mTextUnderCursor();
|
|
|
|
if (!isShortcut && (hasModifier || aEvent->text().isEmpty() || completionPrefix.length() < 3 || eow.contains(aEvent->text().right(1))))
|
|
{
|
|
mCompleter->popup()->hide();
|
|
return;
|
|
}
|
|
|
|
if (completionPrefix != mCompleter->completionPrefix())
|
|
{
|
|
mCompleter->setCompletionPrefix(completionPrefix);
|
|
mCompleter->popup()->setCurrentIndex(mCompleter->completionModel()->index(0, 0));
|
|
}
|
|
|
|
QRect cr = cursorRect();
|
|
cr.setWidth(mCompleter->popup()->sizeHintForColumn(0) + mCompleter->popup()->verticalScrollBar()->sizeHint().width());
|
|
mCompleter->complete(cr);
|
|
}
|
|
|
|
void GSCEditor::focusInEvent(QFocusEvent *aEvent)
|
|
{
|
|
if (mCompleter)
|
|
{
|
|
mCompleter->setWidget(this);
|
|
}
|
|
QTextEdit::focusInEvent(aEvent);
|
|
}
|
|
|
|
void GSCEditor::insertCompletion(const QString &aCompletion)
|
|
{
|
|
if (mCompleter->widget() != this) { return; }
|
|
|
|
QTextCursor cursor = textCursor();
|
|
int extra = aCompletion.length() - mCompleter->completionPrefix().length();
|
|
cursor.movePosition(QTextCursor::Left);
|
|
cursor.movePosition(QTextCursor::EndOfWord);
|
|
cursor.insertText(aCompletion.right(extra));
|
|
setTextCursor(cursor);
|
|
}
|
|
|
|
QString GSCEditor::mTextUnderCursor() const
|
|
{
|
|
QTextCursor cursor = textCursor();
|
|
cursor.select(QTextCursor::WordUnderCursor);
|
|
return cursor.selectedText();
|
|
}
|