RedPanda-CPP/RedPandaIDE/editor.cpp

529 lines
17 KiB
C++
Raw Normal View History

2021-04-06 23:10:57 +08:00
#include "editor.h"
#include <QtCore/QFileInfo>
2021-04-09 17:48:25 +08:00
#include <QFont>
#include <QTextCodec>
2021-04-07 22:44:08 +08:00
#include <QVariant>
2021-04-09 17:48:25 +08:00
#include <QWheelEvent>
2021-04-07 21:13:15 +08:00
#include <memory>
#include "settings.h"
#include "mainwindow.h"
2021-04-11 21:33:08 +08:00
#include "systemconsts.h"
#include <QFileDialog>
#include <QMessageBox>
#include <QDebug>
#include <QMimeData>
2021-05-24 00:41:00 +08:00
#include "qsynedit/highlighter/cpp.h"
2021-05-26 00:04:20 +08:00
#include "HighlighterManager.h"
#include "qsynedit/exporter/synrtfexporter.h"
#include "qsynedit/exporter/synhtmlexporter.h"
#include "qsynedit/Constants.h"
#include <QGuiApplication>
#include <QClipboard>
2021-05-24 00:41:00 +08:00
2021-04-07 21:13:15 +08:00
using namespace std;
2021-04-06 23:10:57 +08:00
2021-04-11 21:33:08 +08:00
SaveException::SaveException(const QString& reason) {
mReason = reason;
}
SaveException::SaveException(const QString&& reason) {
mReason = reason;
}
const QString& SaveException::reason() const noexcept{
return mReason;
}
const char *SaveException::what() const noexcept {
return mReason.toLocal8Bit();
}
2021-05-26 00:04:20 +08:00
int Editor::newfileCount=0;
2021-04-06 23:10:57 +08:00
Editor::Editor(QWidget *parent):
Editor(parent,QObject::tr("untitled"),ENCODING_SYSTEM_DEFAULT,false,true,nullptr)
{
}
2021-04-09 17:48:25 +08:00
Editor::Editor(QWidget *parent, const QString& filename,
const QByteArray& encoding,
2021-04-06 23:10:57 +08:00
bool inProject, bool isNew,
QTabWidget* parentPageControl):
2021-05-24 00:41:00 +08:00
SynEdit(parent),
2021-04-06 23:10:57 +08:00
mFilename(filename),
mEncodingOption(encoding),
2021-04-06 23:10:57 +08:00
mInProject(inProject),
mIsNew(isNew),
mParentPageControl(parentPageControl)
{
2021-04-07 21:13:15 +08:00
if (mFilename.isEmpty()) {
2021-05-26 00:04:20 +08:00
newfileCount++;
mFilename = tr("untitled%1").arg(newfileCount);
2021-04-07 21:13:15 +08:00
}
2021-04-06 23:10:57 +08:00
QFileInfo fileInfo(mFilename);
if (mParentPageControl!=nullptr) {
mParentPageControl->addTab(this,QString());
updateCaption();
}
PSynHighlighter highlighter;
2021-04-06 23:10:57 +08:00
if (!isNew) {
loadFile();
2021-06-07 11:02:03 +08:00
highlighter = highlighterManager.getHighlighter(mFilename);
2021-04-06 23:10:57 +08:00
} else {
if (mEncodingOption == ENCODING_AUTO_DETECT)
mFileEncoding = ENCODING_ASCII;
2021-04-07 21:13:15 +08:00
else
mFileEncoding = mEncodingOption;
2021-06-07 11:02:03 +08:00
highlighter=highlighterManager.getCppHighlighter();
}
if (highlighter) {
setHighlighter(highlighter);
setUseCodeFolding(true);
} else {
setUseCodeFolding(false);
2021-04-06 23:10:57 +08:00
}
2021-04-09 17:48:25 +08:00
2021-06-07 11:02:03 +08:00
applySettings();
2021-06-10 09:34:59 +08:00
connect(this,&SynEdit::statusChanged,this,&Editor::onStatusChanged);
2021-04-07 22:44:08 +08:00
}
2021-04-06 23:10:57 +08:00
2021-04-07 22:44:08 +08:00
Editor::~Editor() {
if (mParentPageControl!=nullptr) {
2021-04-09 17:48:25 +08:00
int index = mParentPageControl->indexOf(this);
mParentPageControl->removeTab(index);
}
this->setParent(0);
2021-04-06 23:10:57 +08:00
}
void Editor::loadFile() {
QFile file(mFilename);
2021-05-24 00:41:00 +08:00
this->lines()->LoadFromFile(file,mEncodingOption,mFileEncoding);
2021-05-24 18:11:07 +08:00
this->setModified(false);
updateCaption();
pMainWindow->updateForEncodingInfo();
2021-04-07 21:13:15 +08:00
}
void Editor::saveFile(const QString &filename) {
QFile file(filename);
2021-05-24 00:41:00 +08:00
this->lines()->SaveToFile(file,mEncodingOption,mFileEncoding);
pMainWindow->updateForEncodingInfo();
}
void Editor::convertToEncoding(const QByteArray &encoding)
{
mEncodingOption = encoding;
setModified(true);
save();
2021-04-06 23:10:57 +08:00
}
2021-04-09 10:08:05 +08:00
bool Editor::save(bool force, bool reparse) {
if (this->mIsNew) {
return saveAs();
}
QFileInfo info(mFilename);
2021-04-11 21:33:08 +08:00
//is this file writable;
if (!force && !info.isWritable()) {
QMessageBox::information(pMainWindow,tr("Fail"),
QString(QObject::tr("File %s is not writable!")));
return false;
}
2021-05-24 00:41:00 +08:00
if (this->modified()|| force) {
2021-04-11 21:33:08 +08:00
try {
saveFile(mFilename);
setModified(false);
mIsNew = false;
this->updateCaption();
2021-04-11 21:33:08 +08:00
} catch (SaveException& exception) {
QMessageBox::information(pMainWindow,tr("Fail"),
exception.reason());
return false;
}
}
if (reparse) {
//todo: reparse the file
}
return true;
}
bool Editor::saveAs(){
2021-04-11 21:33:08 +08:00
QString selectedFileFilter = pSystemConsts->defaultFileFilter();
QString newName = QFileDialog::getSaveFileName(pMainWindow,
2021-04-11 21:33:08 +08:00
tr("Save As"), QString(), pSystemConsts->defaultFileFilters().join(";;"),
&selectedFileFilter);
if (newName.isEmpty()) {
return false;
}
2021-04-11 21:33:08 +08:00
try {
mFilename = newName;
saveFile(mFilename);
2021-04-11 21:33:08 +08:00
mIsNew = false;
setModified(false);
this->updateCaption();
2021-04-11 21:33:08 +08:00
} catch (SaveException& exception) {
QMessageBox::information(pMainWindow,tr("Fail"),
exception.reason());
return false;
}
//todo: update (reassign highlighter)
//todo: remove old file from parser and reparse file
//todo: unmoniter/ monitor file
//todo: update windows caption
//todo: update class browser;
return true;
2021-04-06 23:10:57 +08:00
}
void Editor::activate()
{
if (mParentPageControl!=nullptr)
mParentPageControl->setCurrentWidget(this);
setFocus();
}
2021-04-11 21:33:08 +08:00
const QByteArray& Editor::encodingOption() const noexcept{
return mEncodingOption;
2021-04-06 23:10:57 +08:00
}
2021-04-11 21:33:08 +08:00
void Editor::setEncodingOption(const QByteArray& encoding) noexcept{
mEncodingOption = encoding;
2021-06-20 09:27:37 +08:00
if (!isNew())
loadFile();
else
pMainWindow->updateForEncodingInfo();
}
2021-04-11 21:33:08 +08:00
const QByteArray& Editor::fileEncoding() const noexcept{
2021-04-06 23:10:57 +08:00
return mFileEncoding;
}
2021-04-11 21:33:08 +08:00
const QString& Editor::filename() const noexcept{
2021-04-06 23:10:57 +08:00
return mFilename;
}
2021-04-11 21:33:08 +08:00
bool Editor::inProject() const noexcept{
2021-04-06 23:10:57 +08:00
return mInProject;
}
2021-04-11 21:33:08 +08:00
bool Editor::isNew() const noexcept {
2021-04-06 23:10:57 +08:00
return mIsNew;
}
2021-04-09 17:48:25 +08:00
2021-04-11 21:33:08 +08:00
QTabWidget* Editor::pageControl() noexcept{
2021-04-09 17:48:25 +08:00
return mParentPageControl;
}
void Editor::wheelEvent(QWheelEvent *event) {
if ( (event->modifiers() & Qt::ControlModifier)!=0) {
int size = pSettings->editor().fontSize();
2021-04-09 17:48:25 +08:00
if (event->angleDelta().y()>0) {
size = std::min(99,size+1);
pSettings->editor().setFontSize(size);
pMainWindow->updateEditorSettings();
event->accept();
return;
2021-04-09 17:48:25 +08:00
} else {
size = std::max(2,size-1);
pSettings->editor().setFontSize(size);
pMainWindow->updateEditorSettings();
event->accept();
return;
2021-04-09 17:48:25 +08:00
}
}
SynEdit::wheelEvent(event);
}
void Editor::focusInEvent(QFocusEvent *event)
{
SynEdit::focusInEvent(event);
pMainWindow->updateEditorActions();
pMainWindow->updateStatusbarForLineCol();
pMainWindow->updateForStatusbarModeInfo();
}
void Editor::focusOutEvent(QFocusEvent *event)
{
SynEdit::focusOutEvent(event);
pMainWindow->updateEditorActions();
pMainWindow->updateStatusbarForLineCol();
pMainWindow->updateForStatusbarModeInfo();
}
void Editor::copyToClipboard()
{
if (pSettings->editor().copySizeLimit()) {
if (lines()->count() > pSettings->editor().copyLineLimits()) {
QMessageBox::information(pMainWindow,tr("Fail"),
tr("The text to be copied exceeds count limit!"));
return;
}
if (lines()->getTextLength() > pSettings->editor().copyCharLimits() * 1000) {
QMessageBox::information(pMainWindow,tr("Fail"),
tr("The text to be copied exceeds character limit!"));
return;
}
}
switch(pSettings->editor().copyWithFormatAs()) {
case 1: //HTML
copyAsHTML();
break;;
default:
SynEdit::copyToClipboard();
}
}
void Editor::cutToClipboard()
{
if (pSettings->editor().copySizeLimit()) {
if (lines()->count() > pSettings->editor().copyLineLimits()) {
QMessageBox::information(pMainWindow,tr("Fail"),
tr("The text to be cut exceeds count limit!"));
return;
}
if (lines()->getTextLength() > pSettings->editor().copyCharLimits() * 1000) {
QMessageBox::information(pMainWindow,tr("Fail"),
tr("The text to be cut exceeds character limit!"));
return;
}
}
SynEdit::cutToClipboard();
}
void Editor::copyAsHTML()
{
if (!selAvail())
return;
SynHTMLExporter SynExporterHTML;
SynExporterHTML.setTitle(QFileInfo(mFilename).fileName());
SynExporterHTML.setExportAsText(false);
SynExporterHTML.setUseBackground(true);
SynExporterHTML.setFont(font());
SynExporterHTML.setHighlighter(highlighter());
SynExporterHTML.setCreateHTMLFragment(true);
//SynExporterRTF.OnFormatToken := ExporterFormatToken;
SynExporterHTML.ExportRange(lines(),blockBegin(),blockEnd());
//SynExporterHTML.CopyToClipboard();
QMimeData * mimeData = new QMimeData;
//sethtml will convert buffer to QString , which will cause encoding trouble
mimeData->setData(SynExporterHTML.clipboardFormat(),SynExporterHTML.buffer());
//mimeData->setHtml("<span><style> b {color:red;} </style><b>test</b></span>");
mimeData->setText(selText());
QGuiApplication::clipboard()->clear();
QGuiApplication::clipboard()->setMimeData(mimeData);
2021-04-11 13:55:31 +08:00
}
void Editor::onModificationChanged(bool) {
updateCaption();
2021-04-11 13:55:31 +08:00
}
2021-06-10 09:34:59 +08:00
void Editor::onStatusChanged(SynStatusChanges changes)
{
// if (not (scOpenFile in Changes)) and (fText.Lines.Count <> fLineCount)
// and (fText.Lines.Count <> 0) and ((fLineCount>0) or (fText.Lines.Count>1)) then begin
// if devCodeCompletion.Enabled
// and SameStr(mainForm.ClassBrowser.CurrentFile,FileName) // Don't reparse twice
// then begin
// Reparse;
// end;
// if fText.Focused and devEditor.AutoCheckSyntax and devEditor.CheckSyntaxWhenReturn
// and (fText.Highlighter = dmMain.Cpp) then begin
// mainForm.CheckSyntaxInBack(self);
// end;
// end;
// fLineCount := fText.Lines.Count;
// // scModified is only fired when the modified state changes
if (changes.testFlag(scModified)) {
updateCaption();
}
// if (fTabStopBegin >=0) and (fTabStopY=fText.CaretY) then begin
// if StartsStr(fLineBeforeTabStop,fText.LineText) and EndsStr(fLineAfterTabStop, fText.LineText) then
// fTabStopBegin := Length(fLineBeforeTabStop);
// if fLineAfterTabStop = '' then
// fTabStopEnd := Length(fText.LineText)+1
// else
// fTabStopEnd := Length(fText.LineText) - Length(fLineAfterTabStop);
// fXOffsetSince := fTabStopEnd - fText.CaretX;
// if (fText.CaretX < fTabStopBegin) or (fText.CaretX > (fTabStopEnd+1)) then begin
// fTabStopBegin :=-1;
// end;
// end;
// scSelection includes anything caret related
if (changes.testFlag(SynStatusChange::scSelection)) {
pMainWindow->updateStatusbarForLineCol();
2021-06-10 09:34:59 +08:00
// // Update the function tip
// fFunctionTip.ForceHide := false;
// if Assigned(fFunctionTipTimer) then begin
// if fFunctionTip.Activated and FunctionTipAllowed then begin
// fFunctionTip.Parser := fParser;
// fFunctionTip.FileName := fFileName;
// fFunctionTip.Show;
// end else begin // Reset the timer
// fFunctionTipTimer.Enabled := false;
// fFunctionTipTimer.Enabled := true;
// end;
}
// // Remove error line colors
// if not fIgnoreCaretChange then begin
// if (fErrorLine <> -1) and not fText.SelAvail then begin
// fText.InvalidateLine(fErrorLine);
// fText.InvalidateGutterLine(fErrorLine);
// fErrorLine := -1;
// end;
// end else
// fIgnoreCaretChange := false;
// if fText.SelAvail then begin
// if fText.GetWordAtRowCol(fText.CaretXY) = fText.SelText then begin
// fSelChanged:=True;
// BeginUpdate;
// EndUpdate;
// end else if fSelChanged then begin
// fSelChanged:=False; //invalidate to unhighlight others
// BeginUpdate;
// EndUpdate;
// end;
// end else if fSelChanged then begin
// fSelChanged:=False; //invalidate to unhighlight others
// BeginUpdate;
// EndUpdate;
// end;
// end;
if (changes.testFlag(scInsertMode) | changes.testFlag(scReadOnly))
pMainWindow->updateForStatusbarModeInfo();
pMainWindow->updateEditorActions();
2021-06-10 09:34:59 +08:00
// mainForm.CaretList.AddCaret(self,fText.CaretY,fText.CaretX);
}
2021-06-07 11:02:03 +08:00
void Editor::applySettings()
{
SynEditorOptions options = eoAltSetsColumnMode |
eoDragDropEditing | eoDropFiles | eoKeepCaretX | eoTabsToSpaces |
eoRightMouseMovesCursor | eoScrollByOneLess | eoTabIndent | eoHideShowScrollbars;
//options
2021-06-07 11:02:03 +08:00
options.setFlag(eoAddIndent,pSettings->editor().addIndent());
options.setFlag(eoAutoIndent,pSettings->editor().autoIndent());
options.setFlag(eoTabsToSpaces,pSettings->editor().tabToSpaces());
options.setFlag(eoKeepCaretX,pSettings->editor().keepCaretX());
options.setFlag(eoEnhanceHomeKey,pSettings->editor().enhanceHomeKey());
options.setFlag(eoEnhanceEndKey,pSettings->editor().enhanceEndKey());
options.setFlag(eoHideShowScrollbars,pSettings->editor().autoHideScrollbar());
options.setFlag(eoScrollPastEol,pSettings->editor().scrollPastEol());
options.setFlag(eoScrollPastEof,pSettings->editor().scrollPastEof());
options.setFlag(eoScrollByOneLess,pSettings->editor().scrollByOneLess());
options.setFlag(eoHalfPageScroll,pSettings->editor().halfPageScroll());
2021-06-07 11:02:03 +08:00
setOptions(options);
setTabWidth(pSettings->editor().tabWidth());
setInsertCaret(pSettings->editor().caretForInsert());
setOverwriteCaret(pSettings->editor().caretForOverwrite());
setCaretColor(pSettings->editor().caretColor());
QFont f=QFont(pSettings->editor().fontName(),pSettings->editor().fontSize());
f.setStyleStrategy(QFont::PreferAntialias);
setFont(f);
// Set gutter properties
gutter().setLeftOffset(pSettings->editor().gutterLeftOffset());
gutter().setRightOffset(pSettings->editor().gutterRightOffset());
gutter().setBorderStyle(SynGutterBorderStyle::None);
gutter().setUseFontStyle(pSettings->editor().gutterUseCustomFont());
if (pSettings->editor().gutterUseCustomFont()) {
f=QFont(pSettings->editor().gutterFontName(),pSettings->editor().gutterFontSize());
} else {
f=QFont(pSettings->editor().fontName(),pSettings->editor().fontSize());
}
f.setStyleStrategy(QFont::PreferAntialias);
gutter().setFont(f);
gutter().setDigitCount(pSettings->editor().gutterDigitsCount());
gutter().setVisible(pSettings->editor().gutterVisible());
gutter().setAutoSize(pSettings->editor().gutterAutoSize());
gutter().setShowLineNumbers(pSettings->editor().gutterShowLineNumbers());
gutter().setLeadingZeros(pSettings->editor().gutterAddLeadingZero());
if (pSettings->editor().gutterLineNumbersStartZero())
gutter().setLineNumberStart(0);
else
gutter().setLineNumberStart(1);
//font color
2021-06-07 11:02:03 +08:00
}
void Editor::applyColorScheme(const QString& schemeName)
{
if (highlighter()) {
if (highlighter()->getName() == SYN_HIGHLIGHTER_CPP) {
for (QString name: highlighter()->attributes().keys()) {
PColorSchemeItem item = pColorManager->getItem(schemeName,name);
if (item) {
PSynHighlighterAttribute attr = highlighter()->attributes()[name];
attr->setBackground(item->background());
attr->setForeground(item->foreground());
SynFontStyles styles = SynFontStyle::fsNone;
if (item->bold()) {
styles.setFlag(SynFontStyle::fsBold);
}
if (item->italic()) {
styles.setFlag(SynFontStyle::fsItalic);
}
if (item->underlined()) {
styles.setFlag(SynFontStyle::fsUnderline);
}
if (item->strikeout()) {
styles.setFlag(SynFontStyle::fsStrikeOut);
}
}
}
}
}
PColorSchemeItem item = pColorManager->getItem(schemeName,COLOR_SCHEME_ACTIVE_LINE);
if (item) {
setActiveLineColor(item->background());
}
item = pColorManager->getItem(schemeName,COLOR_SCHEME_GUTTER);
if (item) {
gutter().setTextColor(item->foreground());
gutter().setColor(item->background());
}
item = pColorManager->getItem(schemeName,COLOR_SCHEME_FOLD_LINE);
if (item) {
//todo
}
item = pColorManager->getItem(schemeName,COLOR_SCHEME_INDENT_GUIDE_LINE);
if (item) {
//todo
}
this->invalidate();
}
void Editor::updateCaption(const QString& newCaption) {
if (mParentPageControl==nullptr) {
return;
}
int index = mParentPageControl->indexOf(this);
if (index==-1)
return;
if (newCaption.isEmpty()) {
QString caption = QFileInfo(mFilename).fileName();
2021-05-24 00:41:00 +08:00
if (this->modified()) {
caption.append("[*]");
}
mParentPageControl->setTabText(index,caption);
} else {
mParentPageControl->setTabText(index,newCaption);
}
}