diff options
author | Ken Moore <moorekou@gmail.com> | 2016-04-25 13:08:12 -0400 |
---|---|---|
committer | Ken Moore <moorekou@gmail.com> | 2016-04-25 13:08:12 -0400 |
commit | ed5ecf7ea7a482b4649e66ecb35fbc60af680684 (patch) | |
tree | acc0fa17d228259e847f55c678db9fb0a9b50f0c /src-qt5/desktop-utils/lumina-textedit/PlainTextEditor.cpp | |
parent | Merge branch 'master' of github.com:pcbsd/lumina (diff) | |
download | lumina-ed5ecf7ea7a482b4649e66ecb35fbc60af680684.tar.gz lumina-ed5ecf7ea7a482b4649e66ecb35fbc60af680684.tar.bz2 lumina-ed5ecf7ea7a482b4649e66ecb35fbc60af680684.zip |
Rearrange the Lumina source tree quite a bit:
Now the utilites are arranged by category (core, core-utils, desktop-utils), so all the -utils may be excluded by a package system (or turned into separate packages) as needed.
Diffstat (limited to 'src-qt5/desktop-utils/lumina-textedit/PlainTextEditor.cpp')
-rw-r--r-- | src-qt5/desktop-utils/lumina-textedit/PlainTextEditor.cpp | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/src-qt5/desktop-utils/lumina-textedit/PlainTextEditor.cpp b/src-qt5/desktop-utils/lumina-textedit/PlainTextEditor.cpp new file mode 100644 index 00000000..281c4bec --- /dev/null +++ b/src-qt5/desktop-utils/lumina-textedit/PlainTextEditor.cpp @@ -0,0 +1,269 @@ +//=========================================== +// Lumina-DE source code +// Copyright (c) 2015, Ken Moore +// Available under the 3-clause BSD license +// See the LICENSE file for full details +//=========================================== +#include "PlainTextEditor.h" + +#include <QColor> +#include <QPainter> +#include <QTextBlock> +#include <QFileDialog> +#include <QDebug> + +#include <LuminaUtils.h> + +//============== +// PUBLIC +//============== +PlainTextEditor::PlainTextEditor(QSettings *set, QWidget *parent) : QPlainTextEdit(parent){ + settings = set; + LNW = new LNWidget(this); + showLNW = true; + hasChanges = false; + lastSaveContents.clear(); + matchleft = matchright = -1; + this->setTabStopWidth( 8 * this->fontMetrics().width(" ") ); //8 character spaces per tab (UNIX standard) + //this->setObjectName("PlainTextEditor"); + //this->setStyleSheet("QPlainTextEdit#PlainTextEditor{ }"); + SYNTAX = new Custom_Syntax(settings, this->document()); + connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(LNW_updateWidth()) ); + connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(LNW_highlightLine()) ); + connect(this, SIGNAL(updateRequest(const QRect&, int)), this, SLOT(LNW_update(const QRect&, int)) ); + connect(this, SIGNAL(cursorPositionChanged()), this, SLOT(checkMatchChar()) ); + connect(this, SIGNAL(textChanged()), this, SLOT(textChanged()) ); + LNW_updateWidth(); + LNW_highlightLine(); +} + +PlainTextEditor::~PlainTextEditor(){ + +} + +void PlainTextEditor::showLineNumbers(bool show){ + showLNW = show; + LNW->setVisible(show); + LNW_updateWidth(); +} + +void PlainTextEditor::LoadSyntaxRule(QString type){ + SYNTAX->loadRules(type); + SYNTAX->rehighlight(); +} + +void PlainTextEditor::updateSyntaxColors(){ + SYNTAX->reloadRules(); + SYNTAX->rehighlight(); +} + +//File loading/setting options +void PlainTextEditor::LoadFile(QString filepath){ + this->setWhatsThis(filepath); + this->clear(); + SYNTAX->loadRules( Custom_Syntax::ruleForFile(filepath.section("/",-1)) ); + lastSaveContents = LUtils::readFile(filepath).join("\n"); + this->setPlainText( lastSaveContents ); + hasChanges = false; + emit FileLoaded(this->whatsThis()); +} + +void PlainTextEditor::SaveFile(bool newname){ + //qDebug() << "Save File:" << this->whatsThis(); + if( !this->whatsThis().startsWith("/") || newname ){ + //prompt for a filename/path + QString file = QFileDialog::getSaveFileName(this, tr("Save File"), this->whatsThis(), tr("Text File (*)")); + if(file.isEmpty()){ return; } + this->setWhatsThis(file); + SYNTAX->loadRules( Custom_Syntax::ruleForFile(this->whatsThis().section("/",-1)) ); + SYNTAX->rehighlight(); + } + bool ok = LUtils::writeFile(this->whatsThis(), this->toPlainText().split("\n"), true); + hasChanges = !ok; + if(ok){ lastSaveContents = this->toPlainText(); emit FileLoaded(this->whatsThis()); } + //qDebug() << " - Success:" << ok << hasChanges; +} + +QString PlainTextEditor::currentFile(){ + return this->whatsThis(); +} + +bool PlainTextEditor::hasChange(){ + return hasChanges; +} + +//Functions for managing the line number widget +int PlainTextEditor::LNWWidth(){ + //Get the number of chars we need for line numbers + int lines = this->blockCount(); + if(lines<1){ lines = 1; } + int chars = 1; + while(lines>=10){ chars++; lines/=10; } + return (this->fontMetrics().width("9")*chars); //make sure to add a tiny bit of padding +} + +void PlainTextEditor::paintLNW(QPaintEvent *ev){ + QPainter P(LNW); + //First set the background color + P.fillRect(ev->rect(), QColor("lightgrey")); + //Now determine which line numbers to show (based on the current viewport) + QTextBlock block = this->firstVisibleBlock(); + int bTop = blockBoundingGeometry(block).translated(contentOffset()).top(); + int bBottom; + //Now loop over the blocks (lines) and write in the numbers + P.setPen(Qt::black); //setup the font color + while(block.isValid() && bTop<=ev->rect().bottom()){ //ensure block below top of viewport + bBottom = bTop+blockBoundingRect(block).height(); + if(block.isVisible() && bBottom >= ev->rect().top()){ //ensure block above bottom of viewport + P.drawText(0,bTop, LNW->width(), this->fontMetrics().height(), Qt::AlignRight, QString::number(block.blockNumber()+1) ); + } + //Go to the next block + block = block.next(); + bTop = bBottom; + } +} + +//============== +// PRIVATE +//============== +void PlainTextEditor::clearMatchData(){ + if(matchleft>=0 || matchright>=0){ + QList<QTextEdit::ExtraSelection> sel = this->extraSelections(); + for(int i=0; i<sel.length(); i++){ + if(sel[i].cursor.selectedText().length()==1){ sel.takeAt(i); i--; } + } + this->setExtraSelections(sel); + matchleft = -1; + matchright = -1; + } +} + +void PlainTextEditor::highlightMatch(QChar ch, bool forward, int fromPos, QChar startch){ + if(forward){ matchleft = fromPos; } + else{ matchright = fromPos; } + + int nested = 1; //always start within the first nest (the primary nest) + int tmpFromPos = fromPos; + QString doc = this->toPlainText(); + while( nested>0 && tmpFromPos<doc.length() && ( (tmpFromPos>=fromPos && forward) || ( tmpFromPos<=fromPos && !forward ) ) ){ + if(forward){ + QTextCursor cur = this->document()->find(ch, tmpFromPos); + if(!cur.isNull()){ + nested += doc.mid(tmpFromPos+1, cur.position()-tmpFromPos).count(startch) -1; + if(nested==0){ matchright = cur.position(); } + else{ tmpFromPos = cur.position(); } + }else{ break; } + }else{ + QTextCursor cur = this->document()->find(ch, tmpFromPos, QTextDocument::FindBackward); + if(!cur.isNull()){ + nested += doc.mid(cur.position(), tmpFromPos-cur.position()).count(startch) -1; + if(nested==0){ matchleft = cur.position(); } + else{ tmpFromPos = cur.position()-1; } + }else{ break; } + } + } + + //Now highlight the two characters + QList<QTextEdit::ExtraSelection> sels = this->extraSelections(); + if(matchleft>=0){ + QTextEdit::ExtraSelection sel; + if(matchright>=0){ sel.format.setBackground( QColor(settings->value("colors/bracket-found").toString()) ); } + else{ sel.format.setBackground( QColor(settings->value("colors/bracket-missing").toString()) ); } + QTextCursor cur = this->textCursor(); + cur.setPosition(matchleft); + if(forward){ cur.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); } + else{ cur.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor); } + sel.cursor = cur; + sels << sel; + } + if(matchright>=0){ + QTextEdit::ExtraSelection sel; + if(matchleft>=0){ sel.format.setBackground( QColor(settings->value("colors/bracket-found").toString()) ); } + else{ sel.format.setBackground( QColor(settings->value("colors/bracket-missing").toString()) ); } + QTextCursor cur = this->textCursor(); + cur.setPosition(matchright); + if(!forward){ cur.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor); } + else{ cur.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor); } + sel.cursor = cur; + sels << sel; + } + this->setExtraSelections(sels); +} + +//=================== +// PRIVATE SLOTS +//=================== +//Functions for managing the line number widget +void PlainTextEditor::LNW_updateWidth(){ + if(showLNW){ + this->setViewportMargins( LNWWidth(), 0, 0, 0); //the LNW is contained within the left margin + }else{ + this->setViewportMargins( 0, 0, 0, 0); //the LNW is contained within the left margin + } +} + +void PlainTextEditor::LNW_highlightLine(){ + if(this->isReadOnly()){ return; } + QColor highC = QColor(0,0,0,50); //just darken the line a bit + QTextEdit::ExtraSelection sel; + sel.format.setBackground(highC); + sel.format.setProperty(QTextFormat::FullWidthSelection, true); + sel.cursor = this->textCursor(); + sel.cursor.clearSelection(); //just in case it already has one + setExtraSelections( QList<QTextEdit::ExtraSelection>() << sel ); +} + +void PlainTextEditor::LNW_update(const QRect &rect, int dy){ + if(dy!=0){ LNW->scroll(0,dy); } //make sure to scroll the line widget the same amount as the editor + else{ + //Some other reason we need to repaint the widget + LNW->update(0,rect.y(), LNW->width(), rect.height()); //also repaint the LNW in the same area + } + if(rect.contains(this->viewport()->rect())){ + //Something in the currently-viewed area needs updating - make sure the LNW width is still correct + LNW_updateWidth(); + } +} + +//Function for running the matching routine +void PlainTextEditor::checkMatchChar(){ + clearMatchData(); + int pos = this->textCursor().position(); + QChar ch = this->document()->characterAt(pos); + bool tryback = true; + while(tryback){ + tryback = false; + if(ch==QChar('(')){ highlightMatch(QChar(')'),true, pos, QChar('(') ); } + else if(ch==QChar(')')){ highlightMatch(QChar('('),false, pos, QChar(')') ); } + else if(ch==QChar('{')){ highlightMatch(QChar('}'),true, pos, QChar('{') ); } + else if(ch==QChar('}')){ highlightMatch(QChar('{'),false, pos, QChar('}') ); } + else if(ch==QChar('[')){ highlightMatch(QChar(']'),true, pos, QChar('[') ); } + else if(ch==QChar(']')){ highlightMatch(QChar('['),false, pos, QChar(']') ); } + else if(pos==this->textCursor().position()){ + //Try this one more time - using the previous character instead of the current character + tryback = true; + pos--; + ch = this->document()->characterAt(pos); + } + } //end check for next/previous char +} + +//Functions for notifying the parent widget of changes +void PlainTextEditor::textChanged(){ + //qDebug() << " - Got Text Changed signal"; + bool changed = (lastSaveContents != this->toPlainText()); + if(changed == hasChanges){ return; } //no change + hasChanges = changed; //save for reading later + if(hasChanges){ emit UnsavedChanges( this->whatsThis() ); } + else{ emit FileLoaded(this->whatsThis()); } +} + +//================== +// PROTECTED +//================== +void PlainTextEditor::resizeEvent(QResizeEvent *ev){ + QPlainTextEdit::resizeEvent(ev); //do the normal resize processing + //Now re-adjust the placement of the LNW (within the left margin area) + QRect cGeom = this->contentsRect(); + LNW->setGeometry( QRect(cGeom.left(), cGeom.top(), LNWWidth(), cGeom.height()) ); +} |