mirror of https://gitee.com/cxasm/notepad--.git
4027 lines
114 KiB
C++
Executable File
4027 lines
114 KiB
C++
Executable File
#include "scintillaeditview.h"
|
||
|
||
#include "common.h"
|
||
#include "rgba_icons.h"
|
||
#include "ccnotepad.h"
|
||
#include "styleset.h"
|
||
#include "qtlangset.h"
|
||
#include "findwin.h"
|
||
#include "filemanager.h"
|
||
|
||
#include <Scintilla.h>
|
||
#include <SciLexer.h>
|
||
#include <QImage>
|
||
#include <Qsci/qscilexerpython.h>
|
||
//#include <Qsci/qscilexeravs.h>
|
||
#include <Qsci/qscilexerasm.h>
|
||
#include <Qsci/qscilexerbash.h>
|
||
#include <Qsci/qscilexerbatch.h>
|
||
#include <Qsci/qscilexercmake.h>
|
||
#include <Qsci/qscilexercoffeescript.h>
|
||
#include <Qsci/qscilexercpp.h>
|
||
#include <Qsci/qscilexercsharp.h>
|
||
#include <Qsci/qscilexercss.h>
|
||
#include <Qsci/qscilexercustom.h>
|
||
#include <Qsci/qscilexerd.h>
|
||
#include <Qsci/qscilexerdiff.h>
|
||
#include <Qsci/qscilexeredifact.h>
|
||
#include <Qsci/qscilexerfortran.h>
|
||
#include <Qsci/qscilexerfortran77.h>
|
||
#include <Qsci/qscilexerhtml.h>
|
||
#include <Qsci/qscilexeridl.h>
|
||
#include <Qsci/qscilexerjava.h>
|
||
#include <Qsci/qscilexerjavascript.h>
|
||
#include <Qsci/qscilexerjson.h>
|
||
#include <Qsci/qscilexerlua.h>
|
||
#include <Qsci/qscilexermakefile.h>
|
||
#include <Qsci/qscilexermarkdown.h>
|
||
#include <Qsci/qscilexermatlab.h>
|
||
#include <Qsci/qscilexeroctave.h>
|
||
#include <Qsci/qscilexerpascal.h>
|
||
#include <Qsci/qscilexerperl.h>
|
||
#include <Qsci/qscilexerpo.h>
|
||
#include <Qsci/qscilexerpostscript.h>
|
||
#include <Qsci/qscilexerpov.h>
|
||
#include <Qsci/qscilexerproperties.h>
|
||
#include <Qsci/qscilexerpython.h>
|
||
#include <Qsci/qscilexerr.h>
|
||
#include <Qsci/qscilexerruby.h>
|
||
#include <Qsci/qscilexerspice.h>
|
||
#include <Qsci/qscilexersql.h>
|
||
#include <Qsci/qscilexertcl.h>
|
||
#include <Qsci/qscilexertex.h>
|
||
#include <Qsci/qscilexerverilog.h>
|
||
#include <Qsci/qscilexervhdl.h>
|
||
#include <Qsci/qscilexerxml.h>
|
||
#include <Qsci/qscilexeryaml.h>
|
||
#include <Qsci/qscilexergo.h>
|
||
#include <Qsci/qscilexertext.h>
|
||
#include <Qsci/qscilexernsis.h>
|
||
#include <Qsci/qscilexerglobal.h>
|
||
#include <Qsci/qscilexerrust.h>
|
||
#include <Qsci/qscilexervb.h>
|
||
#include <QScrollBar>
|
||
#include <unordered_set>
|
||
#include <QClipboard>
|
||
#include <QDesktopServices>
|
||
#include <QDebug>
|
||
#include <QMessageBox>
|
||
|
||
|
||
#include <stdexcept>
|
||
#include <mutex>
|
||
|
||
|
||
|
||
// initialize the static variable
|
||
#define DEFAULT_FONT_NAME "Courier New" //"Microsoft YaHei"
|
||
|
||
|
||
int ScintillaEditView::s_tabLens = 4;
|
||
//默认不使用tab,使用space替换
|
||
bool ScintillaEditView::s_noUseTab = true;
|
||
|
||
int ScintillaEditView::s_bigTextSize = 100;
|
||
|
||
|
||
const int ScintillaEditView::_SC_MARGE_LINENUMBER = 0;
|
||
const int ScintillaEditView::_SC_MARGE_SYBOLE = 1;
|
||
const int ScintillaEditView::_SC_MARGE_FOLDER = 2;
|
||
|
||
//大文本是分块显示,务必要把真实的偏移行号给显示出来
|
||
const int SC_BIGTEXT_LINES = 0;
|
||
|
||
const int MAX_PRE_NEXT_TIMES = 30;
|
||
|
||
const int INIT_BIG_RO_TEXT_LINE_WIDTH = 8;
|
||
|
||
#ifdef Q_OS_WIN
|
||
LanguageName ScintillaEditView::langNames[L_EXTERNAL + 1] = {
|
||
{QString("normal"), QString("Normal QString"), QString("Normal text file"), L_TXT, SCLEX_NULL},
|
||
{QString("php"), QString("PHP"), QString("PHP Hypertext Preprocessor file"), L_PHP, SCLEX_HTML},
|
||
{QString("c"), QString("C"), QString("C source file"), L_C, SCLEX_CPP},
|
||
{QString("cpp"), QString("C++"), QString("C++ source file"), L_CPP, SCLEX_CPP},
|
||
{QString("cs"), QString("C#"), QString("C# source file"), L_CS, SCLEX_CPP},
|
||
{QString("objc"), QString("Objective-C"), QString("Objective-C source file"), L_OBJC, SCLEX_CPP},
|
||
{QString("java"), QString("Java"), QString("Java source file"), L_JAVA, SCLEX_CPP},
|
||
{QString("rc"), QString("RC"), QString("Windows Resource file"), L_RC, SCLEX_CPP},
|
||
{QString("html"), QString("HTML"), QString("Hyper Text Markup Language file"), L_HTML, SCLEX_HTML},
|
||
{QString("xml"), QString("XML"), QString("eXtensible Markup Language file"), L_XML, SCLEX_XML},
|
||
{QString("makefile"), QString("Makefile"), QString("Makefile"), L_MAKEFILE, SCLEX_MAKEFILE},
|
||
{QString("pascal"), QString("Pascal"), QString("Pascal source file"), L_PASCAL, SCLEX_PASCAL},
|
||
{QString("batch"), QString("Batch"), QString("Batch file"), L_BATCH, SCLEX_BATCH},
|
||
{QString("ini"), QString("ini"), QString("MS ini file"), L_INI, SCLEX_PROPERTIES},
|
||
{QString("nfo"), QString("NFO"), QString("MSDOS Style/ASCII Art"), L_ASCII, SCLEX_NULL},
|
||
{QString("udf"), QString("udf"), QString("User Defined language file"), L_USER, SCLEX_USER},
|
||
{QString("asp"), QString("ASP"), QString("Active Server Pages script file"), L_ASP, SCLEX_HTML},
|
||
{QString("sql"), QString("SQL"), QString("Structured Query Language file"), L_SQL, SCLEX_SQL},
|
||
{QString("vb"), QString("Visual Basic"), QString("Visual Basic file"), L_VB, SCLEX_VB},
|
||
{QString("javascript"), QString("JavaScript"), QString("JavaScript file"), L_JS, L_JAVASCRIPT},
|
||
{QString("css"), QString("CSS"), QString("Cascade Style Sheets File"), L_CSS, SCLEX_CSS},
|
||
{QString("perl"), QString("Perl"), QString("Perl source file"), L_PERL, SCLEX_PERL},
|
||
{QString("python"), QString("Python"), QString("Python file"), L_PYTHON, SCLEX_PYTHON},
|
||
{QString("lua"), QString("Lua"), QString("Lua source File"), L_LUA, SCLEX_LUA},
|
||
{QString("tex"), QString("TeX"), QString("TeX file"), L_TEX, SCLEX_TEX},
|
||
{QString("fortran"), QString("Fortran free form"), QString("Fortran free form source file"), L_FORTRAN, SCLEX_FORTRAN},
|
||
{QString("bash"), QString("Shell"), QString("Unix script file"), L_BASH, SCLEX_BASH},
|
||
{QString("actionscript"), QString("ActionScript"), QString("Flash ActionScript file"), L_FLASH, SCLEX_CPP},
|
||
{QString("nsis"), QString("NSIS"), QString("Nullsoft Scriptable Install System script file"), L_NSIS, SCLEX_NSIS},
|
||
{QString("tcl"), QString("TCL"), QString("Tool Command Language file"), L_TCL, SCLEX_TCL},
|
||
{QString("lisp"), QString("Lisp"), QString("List Processing language file"), L_LISP, SCLEX_LISP},
|
||
{QString("scheme"), QString("Scheme"), QString("Scheme file"), L_SCHEME, SCLEX_LISP},
|
||
{QString("asm"), QString("Assembly"), QString("Assembly language source file"), L_ASM, SCLEX_ASM},
|
||
{QString("diff"), QString("Diff"), QString("Diff file"), L_DIFF, SCLEX_DIFF},
|
||
{QString("props"), QString("Properties file"), QString("Properties file"), L_PROPS, SCLEX_PROPERTIES},
|
||
{QString("postscript"), QString("PostScript"), QString("PostScript file"), L_PS, SCLEX_PS},
|
||
{QString("ruby"), QString("Ruby"), QString("Ruby file"), L_RUBY, SCLEX_RUBY},
|
||
{QString("smalltalk"), QString("Smalltalk"), QString("Smalltalk file"), L_SMALLTALK, SCLEX_SMALLTALK},
|
||
{QString("vhdl"), QString("VHDL"), QString("VHSIC Hardware Description Language file"), L_VHDL, SCLEX_VHDL},
|
||
{QString("kix"), QString("KiXtart"), QString("KiXtart file"), L_KIX, SCLEX_KIX},
|
||
{QString("autoit"), QString("AutoIt"), QString("AutoIt"), L_AU3, SCLEX_AU3},
|
||
{QString("caml"), QString("CAML"), QString("Categorical Abstract Machine Language"), L_CAML, SCLEX_CAML},
|
||
{QString("ada"), QString("Ada"), QString("Ada file"), L_ADA, SCLEX_ADA},
|
||
{QString("verilog"), QString("Verilog"), QString("Verilog file"), L_VERILOG, SCLEX_VERILOG},
|
||
{QString("matlab"), QString("MATLAB"), QString("MATrix LABoratory"), L_MATLAB, SCLEX_MATLAB},
|
||
{QString("haskell"), QString("Haskell"), QString("Haskell"), L_HASKELL, SCLEX_HASKELL},
|
||
{QString("inno"), QString("Inno Setup"), QString("Inno Setup script"), L_INNO, SCLEX_INNOSETUP},
|
||
{QString("searchResult"), QString("Internal Search"), QString("Internal Search"), L_SEARCHRESULT, SCLEX_SEARCHRESULT},
|
||
{QString("cmake"), QString("CMake"), QString("CMake file"), L_CMAKE, SCLEX_CMAKE},
|
||
{QString("yaml"), QString("YAML"), QString("YAML Ain't Markup Language"), L_YAML, SCLEX_YAML},
|
||
{QString("cobol"), QString("COBOL"), QString("COmmon Business Oriented Language"), L_COBOL, SCLEX_COBOL},
|
||
{QString("gui4cli"), QString("Gui4Cli"), QString("Gui4Cli file"), L_GUI4CLI, SCLEX_GUI4CLI},
|
||
{QString("d"), QString("D"), QString("D programming language"), L_D, SCLEX_D},
|
||
{QString("powershell"), QString("PowerShell"), QString("Windows PowerShell"), L_POWERSHELL, SCLEX_POWERSHELL},
|
||
{QString("r"), QString("R"), QString("R programming language"), L_R, SCLEX_R},
|
||
{QString("jsp"), QString("JSP"), QString("JavaServer Pages script file"), L_JSP, SCLEX_HTML},
|
||
{QString("coffeescript"), QString("CoffeeScript"), QString("CoffeeScript file"), L_COFFEESCRIPT, SCLEX_COFFEESCRIPT},
|
||
{QString("json"), QString("json"), QString("JSON file"), L_JSON, SCLEX_JSON },
|
||
{QString("javascript.js"), QString("JavaScript"), QString("JavaScript file"), L_JAVASCRIPT, SCLEX_CPP },
|
||
{QString("fortran77"), QString("Fortran fixed form"), QString("Fortran fixed form source file"), L_FORTRAN_77, SCLEX_F77},
|
||
{QString("baanc"), QString("BaanC"), QString("BaanC File"), L_BAANC, SCLEX_BAAN },
|
||
{QString("srec"), QString("S-Record"), QString("Motorola S-Record binary data"), L_SREC, SCLEX_SREC},
|
||
{QString("ihex"), QString("Intel HEX"), QString("Intel HEX binary data"), L_IHEX, SCLEX_IHEX},
|
||
{QString("tehex"), QString("Tektronix extended HEX"), QString("Tektronix extended HEX binary data"), L_TEHEX, SCLEX_TEHEX},
|
||
{QString("swift"), QString("Swift"), QString("Swift file"), L_SWIFT, SCLEX_CPP},
|
||
{QString("asn1"), QString("ASN.1"), QString("Abstract Syntax Notation One file"), L_ASN1, SCLEX_ASN1},
|
||
{QString("avs"), QString("AviSynth"), QString("AviSynth scripts files"), L_AVS, SCLEX_AVS},
|
||
{QString("blitzbasic"), QString("BlitzBasic"), QString("BlitzBasic file"), L_BLITZBASIC, SCLEX_BLITZBASIC},
|
||
{QString("purebasic"), QString("PureBasic"), QString("PureBasic file"), L_PUREBASIC, SCLEX_PUREBASIC},
|
||
{QString("freebasic"), QString("FreeBasic"), QString("FreeBasic file"), L_FREEBASIC, SCLEX_FREEBASIC},
|
||
{QString("csound"), QString("Csound"), QString("Csound file"), L_CSOUND, SCLEX_CSOUND},
|
||
{QString("erlang"), QString("Erlang"), QString("Erlang file"), L_ERLANG, SCLEX_ERLANG},
|
||
{QString("escript"), QString("ESCRIPT"), QString("ESCRIPT file"), L_ESCRIPT, SCLEX_ESCRIPT},
|
||
{QString("forth"), QString("Forth"), QString("Forth file"), L_FORTH, SCLEX_FORTH},
|
||
{QString("latex"), QString("LaTeX"), QString("LaTeX file"), L_LATEX, SCLEX_LATEX},
|
||
{QString("mmixal"), QString("MMIXAL"), QString("MMIXAL file"), L_MMIXAL, SCLEX_MMIXAL},
|
||
{QString("nim"), QString("Nim"), QString("Nim file"), L_NIM, SCLEX_NIMROD},
|
||
{QString("nncrontab"), QString("Nncrontab"), QString("extended crontab file"), L_NNCRONTAB, SCLEX_NNCRONTAB},
|
||
{QString("oscript"), QString("OScript"), QString("OScript source file"), L_OSCRIPT, SCLEX_OSCRIPT},
|
||
{QString("rebol"), QString("REBOL"), QString("REBOL file"), L_REBOL, SCLEX_REBOL},
|
||
{QString("registry"), QString("registry"), QString("registry file"), L_REGISTRY, SCLEX_REGISTRY},
|
||
{QString("rust"), QString("Rust"), QString("Rust file"), L_RUST, SCLEX_RUST},
|
||
{QString("spice"), QString("Spice"), QString("spice file"), L_SPICE, SCLEX_SPICE},
|
||
{QString("txt2tags"), QString("txt2tags"), QString("txt2tags file"), L_TXT2TAGS, SCLEX_TXT2TAGS},
|
||
{QString("visualprolog"), QString("Visual Prolog"), QString("Visual Prolog file"), L_VISUALPROLOG, SCLEX_VISUALPROLOG},
|
||
{QString("typescript"), QString("TypeScript"), QString("TypeScript file"), L_TYPESCRIPT, SCLEX_CPP},
|
||
{QString("edifact"), QString("Edifact"), QString("Edifact file"), L_EDIFACT, SCLEX_EDIFACT},
|
||
{QString("markdown"), QString("Markdown"), QString("Markdown file"), L_MARKDOWN, SCLEX_MARKDOWN},
|
||
{QString("octave"), QString("Octave"), QString("Octave file"), L_OCTAVE, SCLEX_OCTAVE},
|
||
{QString("po"), QString("PO"), QString("PO file"), L_PO, SCLEX_PO},
|
||
{QString("pov"), QString("POV"), QString("POV file"), L_POV, SCLEX_POV},
|
||
{QString("avs"), QString("AVS"), QString("AVS file"), L_AVS, SCLEX_AVS},
|
||
{QString("idl"), QString("IDL"), QString("IDL file"), L_IDL, SCLEX_IDL},
|
||
{QString("go"), QString("GO"), QString("GO file"), L_GO, SCLEX_GO},
|
||
{QString("txt"), QString("TEXT"), QString("TXT file"), L_TXT, SCLEX_TXT},
|
||
{QString("ext"), QString("External"), QString("External"), L_EXTERNAL, SCLEX_NULL}
|
||
};
|
||
#endif
|
||
|
||
ScintillaEditView::ScintillaEditView(QWidget *parent,bool isBigText)
|
||
: QsciScintilla(parent), m_NoteWin(nullptr), m_preFirstLineNum(0), m_curPos(0), m_hasHighlight(false), m_bookmarkPng(nullptr), m_styleColorMenu(nullptr), m_isBigText(isBigText), m_curBlockLineStartNum(0)
|
||
{
|
||
init();
|
||
}
|
||
|
||
ScintillaEditView::~ScintillaEditView()
|
||
{
|
||
releaseAllMark();
|
||
|
||
if (m_bookmarkPng != nullptr)
|
||
{
|
||
delete m_bookmarkPng;
|
||
}
|
||
}
|
||
|
||
void ScintillaEditView::setBigTextMode(bool isBigText)
|
||
{
|
||
m_isBigText = isBigText;
|
||
}
|
||
|
||
void ScintillaEditView::setLexer(QsciLexer * lexer)
|
||
{
|
||
QsciScintilla::setLexer(lexer);
|
||
|
||
if (lexer != nullptr && lexer->lexerId() == L_TXT)
|
||
{
|
||
showMargin(_SC_MARGE_FOLDER,false);
|
||
updateThemes();
|
||
}
|
||
else if(lexer != nullptr && lexer->lexerId() != L_TXT)
|
||
{
|
||
showMargin(_SC_MARGE_FOLDER, true);
|
||
updateThemes();
|
||
}
|
||
}
|
||
|
||
void ScintillaEditView::setNoteWidget(QWidget * win)
|
||
{
|
||
CCNotePad* pv = dynamic_cast<CCNotePad*>(win);
|
||
if (pv != nullptr)
|
||
{
|
||
m_NoteWin = pv;
|
||
|
||
if (m_styleColorMenu != nullptr)
|
||
{
|
||
m_styleColorMenu->addAction(tr("Clear Select"), m_NoteWin, &CCNotePad::slot_clearWordHighlight);
|
||
m_styleColorMenu->addAction(tr("Clear All"), m_NoteWin, &CCNotePad::slot_clearMark);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
void ScintillaEditView::updateLineNumbersMargin(bool forcedToHide) {
|
||
if (forcedToHide)
|
||
{
|
||
execute(SCI_SETMARGINWIDTHN, _SC_MARGE_LINENUMBER, (sptr_t)0);
|
||
}
|
||
else
|
||
{
|
||
updateLineNumberWidth(0);
|
||
}
|
||
|
||
}
|
||
|
||
//根据现有滚动条来决定是否更新屏幕线宽长度。每滚动200个单位必须调整line宽
|
||
void ScintillaEditView::autoAdjustLineWidth(int xScrollValue)
|
||
{
|
||
//如果是大文本模式,行号长度目前是固定不变的,不需要动态调整。
|
||
if (m_isBigText)
|
||
{
|
||
return;
|
||
}
|
||
if (std::abs(xScrollValue - m_preFirstLineNum) > 200)
|
||
{
|
||
m_preFirstLineNum = xScrollValue;
|
||
|
||
updateLineNumberWidth(1);
|
||
}
|
||
}
|
||
|
||
void ScintillaEditView::updateLineNumberWidth(int lineNumberMarginDynamicWidth)
|
||
{
|
||
if (!m_isBigText)
|
||
{
|
||
auto linesVisible = execute(SCI_LINESONSCREEN);
|
||
if (linesVisible)
|
||
{
|
||
int nbDigits = 0;
|
||
|
||
if (lineNumberMarginDynamicWidth != 0)
|
||
{
|
||
int firstVisibleLineVis = execute(SCI_GETFIRSTVISIBLELINE);
|
||
int lastVisibleLineVis = linesVisible + firstVisibleLineVis + 1;
|
||
int lastVisibleLineDoc = execute(SCI_DOCLINEFROMVISIBLE, (long)lastVisibleLineVis);
|
||
|
||
nbDigits = nbDigitsFromNbLines(lastVisibleLineDoc);
|
||
nbDigits = nbDigits < 4 ? 4 : nbDigits;
|
||
}
|
||
else
|
||
{
|
||
int nbLines = execute(SCI_GETLINECOUNT);
|
||
nbDigits = nbDigitsFromNbLines(nbLines);
|
||
nbDigits = nbDigits < 4 ? 4 : nbDigits;
|
||
}
|
||
|
||
auto pixelWidth = 6 + nbDigits * execute(SCI_TEXTWIDTH, STYLE_LINENUMBER, reinterpret_cast<sptr_t>("8"));
|
||
execute(SCI_SETMARGINWIDTHN, _SC_MARGE_LINENUMBER, pixelWidth);
|
||
|
||
|
||
}
|
||
}
|
||
else
|
||
{
|
||
int pixelWidth = 6 + INIT_BIG_RO_TEXT_LINE_WIDTH * execute(SCI_TEXTWIDTH, STYLE_LINENUMBER, reinterpret_cast<sptr_t>("8"));
|
||
execute(SCI_SETMARGINWIDTHN, SC_BIGTEXT_LINES, pixelWidth);
|
||
}
|
||
|
||
}
|
||
|
||
|
||
void ScintillaEditView::showMargin(int whichMarge, bool willBeShowed)
|
||
{
|
||
if (whichMarge == _SC_MARGE_LINENUMBER)
|
||
{
|
||
bool forcedToHide = !willBeShowed;
|
||
updateLineNumbersMargin(forcedToHide);
|
||
}
|
||
else
|
||
{
|
||
//后面再来解决像素的问题
|
||
//DPIManager& dpiManager = NppParameters::getInstance()._dpiManager;
|
||
int width = 3;
|
||
if (whichMarge == _SC_MARGE_SYBOLE)
|
||
width = 14;
|
||
else if (whichMarge == _SC_MARGE_FOLDER)
|
||
width = 14;
|
||
execute(SCI_SETMARGINWIDTHN, whichMarge, willBeShowed ? width : 0);
|
||
}
|
||
}
|
||
|
||
sptr_t ScintillaEditView::execute(quint32 Msg, uptr_t wParam, sptr_t lParam) const {
|
||
try {
|
||
return (m_pScintillaFunc) ? m_pScintillaFunc(m_pScintillaPtr, Msg, wParam, lParam) : -1;
|
||
}
|
||
catch (...)
|
||
{
|
||
return -1;
|
||
}
|
||
};
|
||
|
||
|
||
QString ScintillaEditView::getTagByLexerId(int lexerId)
|
||
{
|
||
switch (lexerId)
|
||
{
|
||
case L_PHP:
|
||
return "php";
|
||
|
||
case L_HTML:
|
||
return ("html");
|
||
|
||
case L_ASP:
|
||
return ("asp");
|
||
|
||
case L_JSP:
|
||
return("jsp");
|
||
|
||
case L_C:
|
||
return("c");
|
||
|
||
case L_RC:
|
||
return("rc");
|
||
|
||
case L_CPP:
|
||
return "cpp";
|
||
|
||
case L_OBJC:
|
||
return ("objc");
|
||
|
||
case L_CS:
|
||
return ("csharp");
|
||
|
||
case L_JAVA:
|
||
return ("java");
|
||
|
||
case L_XML:
|
||
return "xml";
|
||
|
||
case L_MAKEFILE:
|
||
return "makefile";
|
||
|
||
case L_PASCAL:
|
||
return "pascal";
|
||
|
||
case L_BATCH:
|
||
return "batch";
|
||
|
||
case L_INI:
|
||
return("ini");
|
||
|
||
case L_ASCII:
|
||
break;
|
||
|
||
case L_USER:
|
||
break;
|
||
|
||
case L_SQL:
|
||
return "sql";
|
||
|
||
case L_VB:
|
||
return "vb";
|
||
|
||
case L_CSS:
|
||
return "css";
|
||
|
||
case L_PERL:
|
||
return "perl";
|
||
|
||
case L_PYTHON:
|
||
return "python";
|
||
|
||
case L_LUA:
|
||
return "lua";
|
||
|
||
case L_TEX:
|
||
break;
|
||
case L_FORTRAN:
|
||
return "fortran";
|
||
|
||
case L_BASH:
|
||
return "bash";
|
||
|
||
case L_FLASH:
|
||
return("flash");
|
||
|
||
case L_NSIS:
|
||
return "nsis";
|
||
|
||
case L_TCL:
|
||
return "tcl";
|
||
|
||
case L_LISP:
|
||
break;
|
||
case L_SCHEME:
|
||
break;
|
||
case L_ASM:
|
||
break;
|
||
case L_DIFF:
|
||
return "diff";
|
||
|
||
case L_PROPS:
|
||
return "props";
|
||
|
||
case L_PS:
|
||
break;
|
||
|
||
case L_R:
|
||
return "r";
|
||
|
||
case L_RUBY:
|
||
return "ruby";
|
||
|
||
case L_SMALLTALK:
|
||
break;
|
||
case L_VHDL:
|
||
return "vhdl";
|
||
|
||
case L_KIX:
|
||
break;
|
||
case L_AU3:
|
||
break;
|
||
case L_CAML:
|
||
break;
|
||
case L_ADA:
|
||
break;
|
||
case L_VERILOG:
|
||
return "verilog";
|
||
|
||
case L_MATLAB:
|
||
return "matlab";
|
||
|
||
case L_HASKELL:
|
||
break;
|
||
case L_INNO:
|
||
break;
|
||
case L_SEARCHRESULT:
|
||
break;
|
||
case L_CMAKE:
|
||
return "cmake";
|
||
|
||
case L_YAML:
|
||
return "yaml";
|
||
|
||
case L_COBOL:
|
||
break;
|
||
case L_GUI4CLI:
|
||
break;
|
||
case L_D:
|
||
break;
|
||
case L_POWERSHELL:
|
||
break;
|
||
|
||
case L_COFFEESCRIPT:
|
||
return "coffeescript";
|
||
|
||
case L_JSON:
|
||
return "json";
|
||
|
||
case L_JAVASCRIPT:
|
||
return ("javascript");
|
||
|
||
case L_FORTRAN_77:
|
||
return "fortran77";
|
||
|
||
case L_BAANC:
|
||
break;
|
||
case L_SREC:
|
||
break;
|
||
case L_IHEX:
|
||
break;
|
||
case L_TEHEX:
|
||
break;
|
||
case L_SWIFT:
|
||
break;
|
||
case L_ASN1:
|
||
break;
|
||
//case L_AVS:
|
||
// return "avs";
|
||
|
||
case L_BLITZBASIC:
|
||
break;
|
||
case L_PUREBASIC:
|
||
break;
|
||
case L_FREEBASIC:
|
||
break;
|
||
case L_CSOUND:
|
||
break;
|
||
case L_ERLANG:
|
||
break;
|
||
case L_ESCRIPT:
|
||
break;
|
||
case L_FORTH:
|
||
break;
|
||
case L_LATEX:
|
||
break;
|
||
case L_MMIXAL:
|
||
break;
|
||
case L_NIM:
|
||
break;
|
||
case L_NNCRONTAB:
|
||
break;
|
||
case L_OSCRIPT:
|
||
break;
|
||
case L_REBOL:
|
||
break;
|
||
case L_REGISTRY:
|
||
break;
|
||
case L_RUST:
|
||
return "rust";
|
||
case L_SPICE:
|
||
return "spice";
|
||
case L_TXT2TAGS:
|
||
break;
|
||
case L_VISUALPROLOG:
|
||
break;
|
||
case L_TYPESCRIPT:
|
||
return("typescript");
|
||
|
||
case L_EXTERNAL:
|
||
break;
|
||
case L_IDL:
|
||
return("idl");
|
||
|
||
case L_GO:
|
||
return("go");
|
||
|
||
case L_GLOBAL:
|
||
return("AllGlobal");
|
||
|
||
case L_TXT:
|
||
return("txt");
|
||
|
||
case L_USER_TXT:
|
||
break;
|
||
case L_USER_CPP:
|
||
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
return "";
|
||
}
|
||
|
||
//status : true 表示存在, false 表示不存在
|
||
//tag,只有在用户自定义语法是,才需要给出。内部自带的语法不需要给出
|
||
//isOrigin:是否原生lexer,即不读取用户修改过的配置风格
|
||
QsciLexer* ScintillaEditView::createLexer(int lexerId, QString tag, bool isOrigin, int styleId)
|
||
{
|
||
QsciLexer* ret = nullptr;
|
||
|
||
switch (lexerId)
|
||
{
|
||
case L_PHP:
|
||
ret = new QsciLexerHTML();
|
||
ret->setLexerTag("php");
|
||
ret->setCommentLineSymbol("//");
|
||
ret->setCommentStart("/*");
|
||
ret->setCommentEnd("*/");
|
||
break;
|
||
case L_HTML:
|
||
ret = new QsciLexerHTML();
|
||
ret->setLexerTag("html");
|
||
break;
|
||
case L_ASP:
|
||
ret = new QsciLexerHTML();
|
||
ret->setLexerTag("asp");
|
||
break;
|
||
case L_JSP:
|
||
ret = new QsciLexerHTML();
|
||
ret->setLexerTag("jsp");
|
||
break;
|
||
case L_C:
|
||
ret = new QsciLexerCPP();
|
||
ret->setLexerTag("c");
|
||
break;
|
||
case L_RC:
|
||
ret = new QsciLexerCPP();
|
||
ret->setLexerTag("rc");
|
||
break;
|
||
case L_CPP:
|
||
ret = new QsciLexerCPP();
|
||
break;
|
||
case L_OBJC:
|
||
ret = new QsciLexerCPP();
|
||
ret->setLexerTag("objc");
|
||
break;
|
||
case L_CS:
|
||
ret = new QsciLexerCSharp();
|
||
ret->setLexerTag("csharp");
|
||
break;
|
||
case L_JAVA:
|
||
ret = new QsciLexerJava();
|
||
ret->setLexerTag("java");
|
||
break;
|
||
case L_XML:
|
||
ret = new QsciLexerXML();
|
||
break;
|
||
case L_MAKEFILE:
|
||
ret = new QsciLexerMakefile();
|
||
break;
|
||
case L_PASCAL:
|
||
ret = new QsciLexerPascal();
|
||
break;
|
||
case L_BATCH:
|
||
ret = new QsciLexerBatch();
|
||
break;
|
||
case L_INI:
|
||
ret = new QsciLexerProperties();
|
||
ret->setLexerTag("ini");
|
||
break;
|
||
case L_ASCII:
|
||
break;
|
||
case L_USER:
|
||
break;
|
||
case L_SQL:
|
||
ret = new QsciLexerSQL();
|
||
break;
|
||
case L_VB:
|
||
ret = new QsciLexerVB();
|
||
break;
|
||
case L_CSS:
|
||
ret = new QsciLexerCSS();
|
||
break;
|
||
case L_PERL:
|
||
ret = new QsciLexerPerl();
|
||
break;
|
||
case L_PYTHON:
|
||
ret = new QsciLexerPython();
|
||
break;
|
||
case L_LUA:
|
||
ret = new QsciLexerLua();
|
||
break;
|
||
case L_TEX:
|
||
break;
|
||
case L_FORTRAN:
|
||
ret = new QsciLexerFortran();
|
||
break;
|
||
case L_BASH:
|
||
ret = new QsciLexerBash();
|
||
break;
|
||
case L_FLASH:
|
||
ret = new QsciLexerCPP();
|
||
ret->setLexerTag("flash");
|
||
break;
|
||
case L_NSIS:
|
||
ret = new QsciLexerNsis();
|
||
break;
|
||
case L_TCL:
|
||
ret = new QsciLexerTCL();
|
||
break;
|
||
case L_LISP:
|
||
break;
|
||
case L_SCHEME:
|
||
break;
|
||
case L_ASM:
|
||
ret = new QsciLexerAsm();
|
||
break;
|
||
case L_DIFF:
|
||
ret = new QsciLexerDiff();
|
||
break;
|
||
case L_PROPS:
|
||
ret = new QsciLexerProperties();
|
||
break;
|
||
case L_PS:
|
||
break;
|
||
case L_R:
|
||
ret = new QsciLexerR();
|
||
break;
|
||
case L_RUBY:
|
||
ret = new QsciLexerRuby();
|
||
break;
|
||
case L_SMALLTALK:
|
||
break;
|
||
case L_VHDL:
|
||
ret = new QsciLexerVHDL();
|
||
break;
|
||
case L_KIX:
|
||
break;
|
||
case L_AU3:
|
||
break;
|
||
case L_CAML:
|
||
break;
|
||
case L_ADA:
|
||
break;
|
||
case L_VERILOG:
|
||
ret = new QsciLexerVerilog();
|
||
break;
|
||
case L_MATLAB:
|
||
ret = new QsciLexerMatlab();
|
||
break;
|
||
case L_HASKELL:
|
||
break;
|
||
case L_INNO:
|
||
break;
|
||
case L_SEARCHRESULT:
|
||
break;
|
||
case L_CMAKE:
|
||
ret = new QsciLexerCMake();
|
||
break;
|
||
case L_YAML:
|
||
ret = new QsciLexerYAML();
|
||
break;
|
||
case L_COBOL:
|
||
break;
|
||
case L_GUI4CLI:
|
||
break;
|
||
case L_D:
|
||
break;
|
||
case L_POWERSHELL:
|
||
break;
|
||
|
||
case L_COFFEESCRIPT:
|
||
ret = new QsciLexerCoffeeScript();
|
||
break;
|
||
case L_JSON:
|
||
ret = new QsciLexerJSON();
|
||
break;
|
||
case L_JAVASCRIPT:
|
||
ret = new QsciLexerJavaScript();
|
||
ret->setLexerTag("javascript");
|
||
break;
|
||
case L_FORTRAN_77:
|
||
ret = new QsciLexerFortran77();
|
||
break;
|
||
case L_BAANC:
|
||
break;
|
||
case L_SREC:
|
||
break;
|
||
case L_IHEX:
|
||
break;
|
||
case L_TEHEX:
|
||
break;
|
||
case L_SWIFT:
|
||
break;
|
||
case L_ASN1:
|
||
break;
|
||
case L_AVS:
|
||
//ret = new QsciLexerAVS();
|
||
break;
|
||
case L_BLITZBASIC:
|
||
break;
|
||
case L_PUREBASIC:
|
||
break;
|
||
case L_FREEBASIC:
|
||
break;
|
||
case L_CSOUND:
|
||
break;
|
||
case L_ERLANG:
|
||
break;
|
||
case L_ESCRIPT:
|
||
break;
|
||
case L_FORTH:
|
||
break;
|
||
case L_LATEX:
|
||
break;
|
||
case L_MMIXAL:
|
||
break;
|
||
case L_NIM:
|
||
break;
|
||
case L_NNCRONTAB:
|
||
break;
|
||
case L_OSCRIPT:
|
||
break;
|
||
case L_REBOL:
|
||
break;
|
||
case L_REGISTRY:
|
||
break;
|
||
case L_RUST:
|
||
ret = new QsciLexerRust();
|
||
break;
|
||
case L_SPICE:
|
||
ret = new QsciLexerSpice();
|
||
break;
|
||
case L_TXT2TAGS:
|
||
break;
|
||
case L_VISUALPROLOG:
|
||
break;
|
||
case L_TYPESCRIPT:
|
||
ret = new QsciLexerCPP();
|
||
ret->setLexerTag("typescript");
|
||
break;
|
||
case L_EXTERNAL:
|
||
break;
|
||
case L_IDL:
|
||
ret = new QsciLexerIDL();
|
||
ret->setLexerTag("idl");
|
||
break;
|
||
case L_GO:
|
||
ret = new QsciLexerGO();
|
||
ret->setLexerTag("go");
|
||
break;
|
||
case L_GLOBAL:
|
||
ret = new QsciLexerGlobal();
|
||
ret->setLexerTag("AllGlobal");
|
||
break;
|
||
case L_TXT:
|
||
ret = new QsciLexerText();
|
||
ret->setLexerTag("txt");
|
||
break;
|
||
case L_USER_TXT:
|
||
//使用txt的语法解析器,但是自定义关键字,自定义tag语言标签。下同
|
||
ret = new QsciLexerText();
|
||
ret->setLexerTag(tag);
|
||
ret->setIsUserDefineKeywords(true);
|
||
break;
|
||
case L_USER_CPP:
|
||
ret = new QsciLexerCPP();
|
||
ret->setLexerTag(tag);
|
||
ret->setIsUserDefineKeywords(true);
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
if (ret != nullptr)
|
||
{
|
||
ret->setLexerId(lexerId);
|
||
|
||
if (!isOrigin)
|
||
{
|
||
QtLangSet::readLangSettings(ret, ret->lexerTag(), styleId);
|
||
}
|
||
else
|
||
{
|
||
//如果是读取原生风格,则只有默认主题具备,其余主题要从模板中加载
|
||
QtLangSet::readLangOriginSettings(ret, ret->lexerTag(), styleId);
|
||
}
|
||
}
|
||
return ret;
|
||
}
|
||
|
||
|
||
void ScintillaEditView::appendMarkRecord(FindRecords * r)
|
||
{
|
||
m_curMarkList.append(r);
|
||
}
|
||
|
||
void ScintillaEditView::releaseAllMark()
|
||
{
|
||
for (int i = 0; i < m_curMarkList.size(); ++i)
|
||
{
|
||
delete m_curMarkList.at(i);
|
||
}
|
||
m_curMarkList.clear();
|
||
}
|
||
|
||
QList<FindRecords *>& ScintillaEditView::getCurMarkRecord()
|
||
{
|
||
return m_curMarkList;
|
||
}
|
||
|
||
////调整颜色
|
||
//void ScintillaEditView::adjuctSkinStyle()
|
||
//{
|
||
// if (StyleSet::m_curStyleId != BLACK_SE)
|
||
// {
|
||
// setMarginsForegroundColor(QColor(0x80, 0x80, 0x80)); //默认0x80, 0x80, 0x80
|
||
// }
|
||
// else
|
||
// {
|
||
// setMarginsForegroundColor(QColor(0xde, 0xde, 0xde)); //默认0x80, 0x80, 0x80
|
||
// }
|
||
// setMarginsBackgroundColor(StyleSet::marginsBackgroundColor);
|
||
//
|
||
// setMarginBackgroundColor(_SC_MARGE_SYBOLE, StyleSet::bookmarkBkColor);
|
||
// setFoldMarginColors(StyleSet::marginsBackgroundColor, StyleSet::marginsBackgroundColor);
|
||
// setStyleOptions();
|
||
//}
|
||
|
||
void ScintillaEditView::setIndentGuide(bool willBeShowed)
|
||
{
|
||
QsciLexer* pLexer = this->lexer();
|
||
if (nullptr == pLexer)
|
||
{
|
||
return;
|
||
}
|
||
const int docIndentMode = (pLexer->lexerId() == L_PYTHON) ? SC_IV_LOOKFORWARD : SC_IV_LOOKBOTH;
|
||
execute(SCI_SETINDENTATIONGUIDES, willBeShowed ? docIndentMode : SC_IV_NONE);
|
||
|
||
QsciScintilla::setIndentGuide(willBeShowed);
|
||
}
|
||
|
||
void ScintillaEditView::init()
|
||
{
|
||
m_pScintillaFunc = (SCINTILLA_FUNC)this->SendScintillaPtrResult(SCI_GETDIRECTFUNCTION);
|
||
m_pScintillaPtr = (SCINTILLA_PTR)this->SendScintillaPtrResult(SCI_GETDIRECTPOINTER);
|
||
|
||
|
||
if (!m_pScintillaFunc)
|
||
{
|
||
throw std::runtime_error("ScintillaEditView::init : SCI_GETDIRECTFUNCTION message failed");
|
||
}
|
||
|
||
if (!m_pScintillaPtr)
|
||
{
|
||
throw std::runtime_error("ScintillaEditView::init : SCI_GETDIRECTPOINTER message failed");
|
||
}
|
||
|
||
if (m_isBigText)
|
||
{
|
||
this->setMarginType(SC_BIGTEXT_LINES, TextMargin);
|
||
}
|
||
else
|
||
{
|
||
//开启行号marge
|
||
setMarginLineNumbers(_SC_MARGE_LINENUMBER, true);
|
||
showMargin(_SC_MARGE_LINENUMBER, true);
|
||
//通过fold发现,尽量使用qscint的功能,因为他做了大量封装和简化
|
||
setFolding(BoxedTreeFoldStyle, _SC_MARGE_FOLDER);
|
||
}
|
||
|
||
//行号、符号、折叠。中间符号留一个很小的间隔。用于圆形的标记符号
|
||
showMargin(_SC_MARGE_SYBOLE, true);
|
||
setMarginType(_SC_MARGE_SYBOLE, QsciScintilla::SymbolMarginColor);
|
||
|
||
m_bookmarkPng = new QPixmap(QString(":/Resources/img/bookmark.png"));
|
||
markerDefine(*m_bookmarkPng, _SC_MARGE_SYBOLE);
|
||
setMarginSensitivity(_SC_MARGE_SYBOLE, true);
|
||
connect(this, &QsciScintilla::marginClicked, this, &ScintillaEditView::slot_bookMarkClicked);
|
||
|
||
|
||
//开始括号匹配,比如html的<>,开启前后这类字段的匹配
|
||
setBraceMatching(SloppyBraceMatch);
|
||
|
||
//自动补全效果不好,不开启20211017
|
||
//setAutoCompletionSource(QsciScintilla::AcsAPIs); //设置源,自动补全所有地方出现的
|
||
//setAutoCompletionCaseSensitivity(true); //设置自动补全大小写敏感
|
||
//setAutoCompletionThreshold(1); //设置每输入一个字符就会出现自动补全的提示
|
||
|
||
//设置字体
|
||
QFont font(DEFAULT_FONT_NAME, 11, QFont::Normal);
|
||
setFont(font);
|
||
setMarginsFont(font);
|
||
|
||
execute(SCI_SETTABWIDTH, ScintillaEditView::s_tabLens);
|
||
|
||
//使用空格替换tab
|
||
setIndentationsUseTabs(!ScintillaEditView::s_noUseTab);
|
||
|
||
//这个无比要设置false,否则双击后高亮单词,拷贝时会拷贝多个选择。
|
||
execute(SCI_SETMULTIPLESELECTION, true);
|
||
execute(SCI_SETADDITIONALSELECTIONTYPING, true);
|
||
|
||
execute(SCI_SETMULTIPASTE, SC_MULTIPASTE_EACH);
|
||
execute(SCI_SETADDITIONALCARETSVISIBLE, true);
|
||
|
||
execute(SCI_SETVIRTUALSPACEOPTIONS, SCVS_RECTANGULARSELECTION);
|
||
|
||
//execute(SCI_SETSELFORE, true, 0x0);
|
||
//execute(SCI_SETSELBACK, true, 0x9bff9b); //0x00ffff原来的黄色
|
||
|
||
//设置查找到Mark的风格。定义其前景颜色和形状
|
||
execute(SCI_INDICSETSTYLE, SCE_UNIVERSAL_FOUND_STYLE_EXT5, INDIC_ROUNDBOX);
|
||
execute(SCI_INDICSETALPHA, SCE_UNIVERSAL_FOUND_STYLE_EXT5, 130);
|
||
execute(SCI_INDICSETUNDER, SCE_UNIVERSAL_FOUND_STYLE_EXT5, false);
|
||
//execute(SCI_INDICSETFORE, SCE_UNIVERSAL_FOUND_STYLE_EXT5, 0x00ffff);
|
||
|
||
//设置查找到Mark的风格。定义其前景颜色和形状
|
||
execute(SCI_INDICSETSTYLE, SCE_UNIVERSAL_FOUND_STYLE_EXT4, INDIC_ROUNDBOX);
|
||
execute(SCI_INDICSETALPHA, SCE_UNIVERSAL_FOUND_STYLE_EXT4, 130);
|
||
execute(SCI_INDICSETUNDER, SCE_UNIVERSAL_FOUND_STYLE_EXT4, false);
|
||
//execute(SCI_INDICSETFORE, SCE_UNIVERSAL_FOUND_STYLE_EXT4, 0xffff00);
|
||
|
||
//设置查找到Mark的风格。定义其前景颜色和形状
|
||
execute(SCI_INDICSETSTYLE, SCE_UNIVERSAL_FOUND_STYLE_EXT3, INDIC_ROUNDBOX);
|
||
execute(SCI_INDICSETALPHA, SCE_UNIVERSAL_FOUND_STYLE_EXT3, 130);
|
||
execute(SCI_INDICSETUNDER, SCE_UNIVERSAL_FOUND_STYLE_EXT3, false);
|
||
//execute(SCI_INDICSETFORE, SCE_UNIVERSAL_FOUND_STYLE_EXT3, 0x0080ff);
|
||
|
||
//设置查找到Mark的风格。定义其前景颜色和形状
|
||
execute(SCI_INDICSETSTYLE, SCE_UNIVERSAL_FOUND_STYLE_EXT2, INDIC_ROUNDBOX);
|
||
execute(SCI_INDICSETALPHA, SCE_UNIVERSAL_FOUND_STYLE_EXT2, 130);
|
||
execute(SCI_INDICSETUNDER, SCE_UNIVERSAL_FOUND_STYLE_EXT2, false);
|
||
//execute(SCI_INDICSETFORE, SCE_UNIVERSAL_FOUND_STYLE_EXT2, 0xff0080);
|
||
|
||
//设置查找到Mark的风格。定义其前景颜色和形状
|
||
execute(SCI_INDICSETSTYLE, SCE_UNIVERSAL_FOUND_STYLE_EXT1, INDIC_ROUNDBOX);
|
||
execute(SCI_INDICSETALPHA, SCE_UNIVERSAL_FOUND_STYLE_EXT1, 130);
|
||
execute(SCI_INDICSETUNDER, SCE_UNIVERSAL_FOUND_STYLE_EXT1, false);
|
||
//execute(SCI_INDICSETFORE, SCE_UNIVERSAL_FOUND_STYLE_EXT1, 0xff8000);
|
||
|
||
//下面这两个是HTML文件的tag高亮的表示。
|
||
execute(SCI_INDICSETSTYLE, SCE_UNIVERSAL_TAGMATCH, INDIC_STRAIGHTBOX);
|
||
execute(SCI_INDICSETALPHA, SCE_UNIVERSAL_TAGMATCH, 100);
|
||
execute(SCI_INDICSETUNDER, SCE_UNIVERSAL_TAGMATCH, false);
|
||
//execute(SCI_INDICSETFORE, SCE_UNIVERSAL_TAGMATCH, 0xff0080);
|
||
|
||
execute(SCI_INDICSETSTYLE, SCE_UNIVERSAL_TAGATTR, INDIC_STRAIGHTBOX);
|
||
execute(SCI_INDICSETALPHA, SCE_UNIVERSAL_TAGATTR, 100);
|
||
execute(SCI_INDICSETUNDER, SCE_UNIVERSAL_TAGATTR, false);
|
||
//execute(SCI_INDICSETFORE, SCE_UNIVERSAL_TAGATTR, 0x00ffff);
|
||
|
||
//双击后同样的字段进行高亮
|
||
execute(SCI_INDICSETSTYLE, SCE_UNIVERSAL_FOUND_STYLE_SMART, INDIC_ROUNDBOX);
|
||
execute(SCI_INDICSETALPHA, SCE_UNIVERSAL_FOUND_STYLE_SMART, 100);
|
||
execute(SCI_INDICSETUNDER, SCE_UNIVERSAL_FOUND_STYLE_SMART, false);
|
||
//execute(SCI_INDICSETFORE, SCE_UNIVERSAL_FOUND_STYLE_SMART, 0x00ff00);
|
||
|
||
//设置空白字符的默认前景色
|
||
//execute(SCI_SETWHITESPACEFORE, true, 0x6ab5ff);
|
||
execute(SCI_SETWHITESPACESIZE,3);
|
||
|
||
setCaretLineVisible(true);
|
||
|
||
//统一设置全局前景、背景、字体大小三个要素
|
||
updateThemes();
|
||
|
||
//记住position变化。不能使用cursorPositionChanged,因为他的列考虑uft8字符,中文一个也算1个,每次列不一定相等。
|
||
//要使用自定义的cursorPosChange,跟踪的是SCI_GETCURRENTPOS 的值。换行才会触发这个cursorPosChange。自定义的信号
|
||
connect(this,&QsciScintilla::cursorPosChange,this,&ScintillaEditView::slot_linePosChanged, Qt::QueuedConnection);
|
||
|
||
connect(this, &QsciScintilla::selectionChanged, this, &ScintillaEditView::slot_clearHightWord, Qt::QueuedConnection);
|
||
|
||
execute(SCI_SETSCROLLWIDTH, 1);
|
||
|
||
connect(this->verticalScrollBar(), &QScrollBar::valueChanged, this, &ScintillaEditView::slot_scrollYValueChange);
|
||
connect(this, &ScintillaEditView::delayWork, this,&ScintillaEditView::slot_delayWork, Qt::QueuedConnection);
|
||
|
||
//设置换行符号的格式
|
||
#if defined(Q_OS_WIN)
|
||
execute(SCI_SETEOLMODE, SC_EOL_CRLF);
|
||
#elif !defined(Q_OS_MAC)
|
||
execute(SCI_SETEOLMODE, SC_EOL_CR);
|
||
#elif !defined(Q_OS_UNIX)
|
||
execute(SCI_SETEOLMODE, SC_EOL_LF);
|
||
#endif
|
||
|
||
//开启新行自动缩进
|
||
setAutoIndent(true);
|
||
|
||
//开启后可以保证长行在滚动条下完整显示
|
||
execute(SCI_SETSCROLLWIDTHTRACKING, true);
|
||
|
||
}
|
||
|
||
//大文本不能显示行号,其实显示的是每一行的地址。
|
||
//因为要跳转,所以没法实时计算当前位置所在的行号
|
||
void ScintillaEditView::showBigTextLineAddr(qint64 fileOffset)
|
||
{
|
||
int nbDigits = 0;
|
||
|
||
if (fileOffset < 0xffffffff)
|
||
{
|
||
nbDigits = 9;
|
||
}
|
||
else
|
||
{
|
||
nbDigits = 13;
|
||
}
|
||
char* lineString = new char[17];
|
||
memset(lineString, 0, 17);
|
||
|
||
auto pixelWidth = 6 + nbDigits * execute(SCI_TEXTWIDTH, STYLE_LINENUMBER, reinterpret_cast<sptr_t>("8"));
|
||
this->execute(SCI_SETMARGINWIDTHN, SC_BIGTEXT_LINES, pixelWidth);
|
||
|
||
int lineNums = this->lines();
|
||
int lineLength = 0;
|
||
|
||
int style = STYLE_LINENUMBER;
|
||
|
||
for (int i = 0; i < lineNums; ++i)
|
||
{
|
||
memset(lineString, 0, 17);
|
||
|
||
lineLength = this->lineLength(i);
|
||
|
||
if (fileOffset < 0xffffffff)
|
||
{
|
||
sprintf(lineString, "%08llX ", fileOffset);
|
||
}
|
||
else
|
||
{
|
||
sprintf(lineString, "%012llX ", fileOffset);
|
||
}
|
||
|
||
QString num(lineString);
|
||
|
||
fileOffset += lineLength;
|
||
this->setMarginText(i, num, style);
|
||
}
|
||
|
||
delete[]lineString;
|
||
}
|
||
|
||
void ScintillaEditView::clearSuperBitLineCache()
|
||
{
|
||
m_addrLineNumMap.clear();
|
||
}
|
||
//20230116新增,尽可能的还是显示行号。如果发生了跳转,则没有办法计算前面的行号,
|
||
//则只能显示地址。如果没跳转,而是动态顺序翻页,则可以显示行号
|
||
//20230201发现一个问题。底层qscint是按照utf8字节流来计算字符大小的。如果原始文件的编码
|
||
//不是utf8,比如GBK LE等,则大小是不能统一的。这是一个显示问题,但是不影响什么。
|
||
//通过this->lineLength(i);来计算是以utf8计算。
|
||
void ScintillaEditView::showBigTextLineAddr(qint64 fileOffset, qint64 fileEndOffset)
|
||
{
|
||
int nbDigits = 0;
|
||
|
||
if (fileOffset < 0xffffffff)
|
||
{
|
||
nbDigits = INIT_BIG_RO_TEXT_LINE_WIDTH;
|
||
}
|
||
else
|
||
{
|
||
nbDigits = 12;
|
||
}
|
||
char* lineString = new char[17];
|
||
memset(lineString, 0, 17);
|
||
|
||
auto pixelWidth = 6 + nbDigits * execute(SCI_TEXTWIDTH, STYLE_LINENUMBER, reinterpret_cast<sptr_t>("8"));
|
||
this->execute(SCI_SETMARGINWIDTHN, SC_BIGTEXT_LINES, pixelWidth);
|
||
|
||
int lineNums = this->lines();
|
||
int lineLength = 0;
|
||
|
||
int style = STYLE_LINENUMBER;
|
||
|
||
quint32 startLineNumOffset = 0;
|
||
quint32 endLineNumOffset = 0;
|
||
|
||
bool isShowLineNum = false; //显示行号还是地址
|
||
bool startLineExist = false;
|
||
bool endLineExist = false;
|
||
|
||
|
||
if (fileOffset == 0)
|
||
{
|
||
isShowLineNum = true;
|
||
startLineExist = true;
|
||
m_addrLineNumMap.insert(0, 1); //0地址对应第1行
|
||
m_addrLineNumMap.insert(fileEndOffset, lineNums+1); //fileEndOffset地址对应最后一行
|
||
}
|
||
|
||
if (lineNums >= 1)
|
||
{
|
||
if (m_addrLineNumMap.contains(fileOffset))
|
||
{
|
||
isShowLineNum = true;
|
||
startLineExist = true;
|
||
|
||
startLineNumOffset = m_addrLineNumMap.value(fileOffset);
|
||
}
|
||
else if (m_addrLineNumMap.contains(fileEndOffset))
|
||
{
|
||
isShowLineNum = true;
|
||
endLineExist = true;
|
||
|
||
endLineNumOffset = m_addrLineNumMap.value(fileEndOffset);
|
||
}
|
||
}
|
||
|
||
//不存在行号,只能显示地址
|
||
if (!isShowLineNum)
|
||
{
|
||
for (int i = 0; i < lineNums; ++i)
|
||
{
|
||
memset(lineString, 0, 17);
|
||
|
||
lineLength = this->lineLength(i);
|
||
|
||
if (fileOffset < 0xffffffff)
|
||
{
|
||
sprintf(lineString, "%08llX ", fileOffset);
|
||
}
|
||
else
|
||
{
|
||
sprintf(lineString, "%012llX ", fileOffset);
|
||
}
|
||
|
||
QString num(lineString);
|
||
|
||
fileOffset += lineLength;
|
||
this->setMarginText(i, num, style);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
//首行地址存在,从头到尾增加行号
|
||
if (startLineExist)
|
||
{
|
||
|
||
for (int i = 0; i < lineNums; ++i)
|
||
{
|
||
if (i == (lineNums - 1))
|
||
{
|
||
//m_addrLineNumMap.insert(fileOffset, startLineNumOffset + i);
|
||
m_addrLineNumMap.insert(fileEndOffset, startLineNumOffset + i);
|
||
}
|
||
|
||
memset(lineString, 0, 17);
|
||
//lineLength = this->lineLength(i);
|
||
sprintf(lineString, "%08lld ", startLineNumOffset + i);
|
||
QString num(lineString);
|
||
|
||
//fileOffset += lineLength;
|
||
this->setMarginText(i, num, style);
|
||
}
|
||
}
|
||
else if (endLineExist)
|
||
{
|
||
|
||
startLineNumOffset = endLineNumOffset - lineNums;
|
||
|
||
for (int i = 0; i < lineNums; ++i)
|
||
{
|
||
if (i == 0)
|
||
{
|
||
//m_addrLineNumMap.insert(fileOffset, startLineNumOffset + i);
|
||
m_addrLineNumMap.insert(fileOffset, startLineNumOffset + i);
|
||
}
|
||
|
||
memset(lineString, 0, 17);
|
||
//lineLength = this->lineLength(i);
|
||
sprintf(lineString, "%08lld ", startLineNumOffset + i);
|
||
QString num(lineString);
|
||
|
||
//fileOffset += lineLength;
|
||
this->setMarginText(i, num, style);
|
||
}
|
||
}
|
||
}
|
||
|
||
delete[]lineString;
|
||
}
|
||
|
||
//大文本只读模式下,显示其文本
|
||
void ScintillaEditView::showBigTextRoLineNum(BigTextEditFileMgr* txtFile, int blockIndex)
|
||
{
|
||
|
||
BlockIndex bi = txtFile->blocks.at(blockIndex);
|
||
|
||
int nbDigits = 0;
|
||
|
||
if (bi.fileOffset < 0xffffffff)
|
||
{
|
||
nbDigits = INIT_BIG_RO_TEXT_LINE_WIDTH;
|
||
}
|
||
else
|
||
{
|
||
nbDigits = 12;
|
||
}
|
||
char* lineString = new char[17];
|
||
memset(lineString, 0, 17);
|
||
|
||
auto pixelWidth = 6 + nbDigits * execute(SCI_TEXTWIDTH, STYLE_LINENUMBER, reinterpret_cast<sptr_t>("8"));
|
||
this->execute(SCI_SETMARGINWIDTHN, SC_BIGTEXT_LINES, pixelWidth);
|
||
|
||
int lineNums = this->lines();
|
||
|
||
qint64 curLineNum = bi.lineNumStart + 1;//行号从1开始
|
||
|
||
int style = STYLE_LINENUMBER;
|
||
|
||
for (int i = 0; i < lineNums; ++i)
|
||
{
|
||
memset(lineString, 0, 17);
|
||
|
||
if (bi.fileOffset < 0xffffffff)
|
||
{
|
||
sprintf(lineString, "%08lld ", curLineNum+i);
|
||
}
|
||
else
|
||
{
|
||
sprintf(lineString, "%012lld ", curLineNum + i);
|
||
}
|
||
|
||
QString num(lineString);
|
||
this->setMarginText(i, num, style);
|
||
}
|
||
|
||
delete[]lineString;
|
||
}
|
||
|
||
void ScintillaEditView::bookmarkNext(bool forwardScan)
|
||
{
|
||
size_t lineno = this->getCurrentLineNumber();
|
||
int sci_marker = SCI_MARKERNEXT;
|
||
size_t lineStart = lineno + 1; //Scan starting from next line
|
||
int lineRetry = 0; //If not found, try from the beginning
|
||
if (!forwardScan)
|
||
{
|
||
lineStart = lineno - 1; //Scan starting from previous line
|
||
lineRetry = int(this->execute(SCI_GETLINECOUNT)); //If not found, try from the end
|
||
sci_marker = SCI_MARKERPREVIOUS;
|
||
}
|
||
int nextLine = int(this->execute(sci_marker, lineStart, 1 << _SC_MARGE_SYBOLE));
|
||
if (nextLine < 0)
|
||
nextLine = int(this->execute(sci_marker, lineRetry, 1 << _SC_MARGE_SYBOLE));
|
||
|
||
if (nextLine < 0)
|
||
return;
|
||
|
||
this->execute(SCI_ENSUREVISIBLEENFORCEPOLICY, nextLine);
|
||
this->execute(SCI_GOTOLINE, nextLine);
|
||
}
|
||
|
||
void ScintillaEditView::bookmarkAdd(intptr_t lineno) const {
|
||
if (lineno == -1)
|
||
lineno = getCurrentLineNumber();
|
||
if (!bookmarkPresent(lineno))
|
||
this->execute(SCI_MARKERADD, lineno, _SC_MARGE_SYBOLE);
|
||
}
|
||
|
||
void ScintillaEditView::bookmarkDelete(size_t lineno) const {
|
||
if (lineno == -1)
|
||
lineno = this->getCurrentLineNumber();
|
||
while (bookmarkPresent(lineno))
|
||
this->execute(SCI_MARKERDELETE, lineno, _SC_MARGE_SYBOLE);
|
||
}
|
||
|
||
bool ScintillaEditView::bookmarkPresent(intptr_t lineno) const {
|
||
if (lineno == -1)
|
||
lineno = this->getCurrentLineNumber();
|
||
int state = this->execute(SCI_MARKERGET, lineno);
|
||
return ((state & (1 << _SC_MARGE_SYBOLE)) != 0);
|
||
}
|
||
|
||
void ScintillaEditView::bookmarkToggle(intptr_t lineno) const {
|
||
if (lineno == -1)
|
||
lineno = this->getCurrentLineNumber();
|
||
|
||
if (bookmarkPresent(lineno))
|
||
bookmarkDelete(lineno);
|
||
else
|
||
bookmarkAdd(lineno);
|
||
}
|
||
|
||
void ScintillaEditView::bookmarkClearAll() const {
|
||
this->execute(SCI_MARKERDELETEALL, _SC_MARGE_SYBOLE);
|
||
}
|
||
|
||
void ScintillaEditView::slot_bookMarkClicked(int margin, int line, Qt::KeyboardModifiers state)
|
||
{
|
||
if (margin == _SC_MARGE_SYBOLE)
|
||
{
|
||
bookmarkToggle(line);
|
||
}
|
||
}
|
||
|
||
std::mutex mark_mutex;
|
||
|
||
QString ScintillaEditView::getMarkedLine(int ln)
|
||
{
|
||
auto lineLen = this->execute(SCI_LINELENGTH, ln);
|
||
auto lineBegin = this->execute(SCI_POSITIONFROMLINE, ln);
|
||
|
||
return this->getGenericTextAsQString(lineBegin, lineBegin + lineLen);
|
||
}
|
||
|
||
void ScintillaEditView::deleteMarkedline(int ln)
|
||
{
|
||
int lineLen = static_cast<int32_t>(this->execute(SCI_LINELENGTH, ln));
|
||
int lineBegin = static_cast<int32_t>(this->execute(SCI_POSITIONFROMLINE, ln));
|
||
bookmarkDelete(ln);
|
||
|
||
QByteArray str2replace;
|
||
|
||
this->replaceTarget(str2replace, lineBegin, lineBegin + lineLen);
|
||
}
|
||
|
||
void str2Cliboard(QString& text)
|
||
{
|
||
QClipboard* clip = QApplication::clipboard();
|
||
clip->setText(text);
|
||
}
|
||
|
||
void ScintillaEditView::cutMarkedLines()
|
||
{
|
||
std::lock_guard<std::mutex> lock(mark_mutex);
|
||
|
||
int lastLine = this->lastZeroBasedLineNumber();
|
||
QString globalStr;
|
||
|
||
this->execute(SCI_BEGINUNDOACTION);
|
||
for (int i = lastLine; i >= 0; i--)
|
||
{
|
||
if (bookmarkPresent(i))
|
||
{
|
||
QString currentStr = getMarkedLine(i) + globalStr;
|
||
globalStr = currentStr;
|
||
|
||
deleteMarkedline(i);
|
||
}
|
||
}
|
||
this->execute(SCI_ENDUNDOACTION);
|
||
str2Cliboard(globalStr);
|
||
}
|
||
|
||
void ScintillaEditView::copyMarkedLines()
|
||
{
|
||
int lastLine = this->lastZeroBasedLineNumber();
|
||
QString globalStr;
|
||
for (int i = lastLine; i >= 0; i--)
|
||
{
|
||
if (bookmarkPresent(i))
|
||
{
|
||
QString currentStr = getMarkedLine(i) + globalStr;
|
||
globalStr = currentStr;
|
||
}
|
||
}
|
||
str2Cliboard(globalStr);
|
||
}
|
||
|
||
void ScintillaEditView::replaceMarkedline(int ln, QByteArray& str)
|
||
{
|
||
int lineBegin = static_cast<int32_t>(this->execute(SCI_POSITIONFROMLINE, ln));
|
||
int lineEnd = static_cast<int32_t>(this->execute(SCI_GETLINEENDPOSITION, ln));
|
||
|
||
this->replaceTarget(str, lineBegin, lineEnd);
|
||
}
|
||
|
||
void ScintillaEditView::pasteToMarkedLines()
|
||
{
|
||
std::lock_guard<std::mutex> lock(mark_mutex);
|
||
|
||
QClipboard* clip = QApplication::clipboard();
|
||
const QMimeData *mimeData = clip->mimeData();
|
||
if (!mimeData->hasText())
|
||
{
|
||
return;
|
||
}
|
||
|
||
int lastLine = this->lastZeroBasedLineNumber();
|
||
QString clipboardStr = clip->text();
|
||
|
||
this->execute(SCI_BEGINUNDOACTION);
|
||
for (int i = lastLine; i >= 0; i--)
|
||
{
|
||
if (bookmarkPresent(i))
|
||
{
|
||
QByteArray text = clipboardStr.toUtf8();
|
||
replaceMarkedline(i, text);
|
||
}
|
||
}
|
||
this->execute(SCI_ENDUNDOACTION);
|
||
}
|
||
|
||
//Y方向滚动条值变化后的槽函数
|
||
void ScintillaEditView::slot_scrollYValueChange(int value)
|
||
{
|
||
//使用滚动条翻页的效果并不好。暂时不放开
|
||
/*bool changePage = false;
|
||
|
||
qDebug() << value << verticalScrollBar()->maximum() << verticalScrollBar()->minimum();
|
||
|
||
if (value >= this->verticalScrollBar()->maximum())
|
||
{
|
||
if (m_NoteWin != nullptr)
|
||
{
|
||
changePage = m_NoteWin->nextPage(this);
|
||
}
|
||
}
|
||
else if (value == this->verticalScrollBar()->minimum())
|
||
{
|
||
if (m_NoteWin != nullptr)
|
||
{
|
||
changePage = m_NoteWin->prePage(this);
|
||
}
|
||
}*/
|
||
|
||
if (value >= this->verticalScrollBar()->maximum())
|
||
{
|
||
if (m_NoteWin != nullptr)
|
||
{
|
||
m_NoteWin->showChangePageTips(this);
|
||
}
|
||
}
|
||
else if (value == this->verticalScrollBar()->minimum())
|
||
{
|
||
if (m_NoteWin != nullptr)
|
||
{
|
||
m_NoteWin->showChangePageTips(this);
|
||
}
|
||
}
|
||
|
||
slot_delayWork();
|
||
autoAdjustLineWidth(value);
|
||
|
||
}
|
||
|
||
|
||
void ScintillaEditView::deleteMarkedLines(bool isMarked)
|
||
{
|
||
std::lock_guard<std::mutex> lock(mark_mutex);
|
||
|
||
int lastLine = this->lastZeroBasedLineNumber();
|
||
|
||
this->execute(SCI_BEGINUNDOACTION);
|
||
for (int i = lastLine; i >= 0; i--)
|
||
{
|
||
if (bookmarkPresent(i) == isMarked)
|
||
deleteMarkedline(i);
|
||
}
|
||
this->execute(SCI_ENDUNDOACTION);
|
||
}
|
||
|
||
void ScintillaEditView::inverseMarks()
|
||
{
|
||
int lastLine = this->lastZeroBasedLineNumber();
|
||
for (int i = 0; i <= lastLine; ++i)
|
||
{
|
||
if (bookmarkPresent(i))
|
||
{
|
||
bookmarkDelete(i);
|
||
}
|
||
else
|
||
{
|
||
bookmarkAdd(i);
|
||
}
|
||
}
|
||
}
|
||
|
||
void ScintillaEditView::slot_linePosChanged(int /*line*/, int pos)
|
||
{
|
||
//避免循环触发的slot_positionChanged
|
||
if (m_curPos == pos)
|
||
{
|
||
return;
|
||
}
|
||
|
||
m_prePosRecord.append(pos);
|
||
|
||
if (m_prePosRecord.size() >= MAX_PRE_NEXT_TIMES)
|
||
{
|
||
m_prePosRecord.takeFirst();
|
||
}
|
||
}
|
||
|
||
//回退一个位置
|
||
bool ScintillaEditView::gotoPrePos()
|
||
{
|
||
if (m_prePosRecord.size() > 1)
|
||
{
|
||
int v = m_prePosRecord.takeLast();
|
||
m_nextPosRecord.push_front(v);
|
||
|
||
if (m_nextPosRecord.size() >= MAX_PRE_NEXT_TIMES)
|
||
{
|
||
m_nextPosRecord.takeLast();
|
||
}
|
||
|
||
int pos = m_prePosRecord.last();
|
||
|
||
//下面这个函数比较坑,里面要循环Index次,每次返回的index值是不一样的。只能使用SCI_GOTOPOS可自定义的信号
|
||
//setCursorPosition(pos.line, pos.index);
|
||
|
||
//会循环触发slot_positionChanged
|
||
this->execute(SCI_GOTOPOS, pos);
|
||
m_curPos = pos;
|
||
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
bool ScintillaEditView::gotoNextPos()
|
||
{
|
||
if (m_nextPosRecord.size() > 0)
|
||
{
|
||
int pos = m_nextPosRecord.takeFirst();
|
||
m_prePosRecord.append(pos);
|
||
this->execute(SCI_GOTOPOS, pos);
|
||
m_curPos = pos;
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
const int MAXLINEHIGHLIGHT = 400;
|
||
|
||
void ScintillaEditView::clearIndicator(int indicatorNumber) {
|
||
size_t docStart = 0;
|
||
size_t docEnd = length();
|
||
execute(SCI_SETINDICATORCURRENT, indicatorNumber);
|
||
execute(SCI_INDICATORCLEARRANGE, docStart, docEnd - docStart);
|
||
};
|
||
|
||
void ScintillaEditView::slot_clearHightWord()
|
||
{
|
||
if (m_hasHighlight)
|
||
{
|
||
m_hasHighlight = false;
|
||
clearIndicator(SCE_UNIVERSAL_FOUND_STYLE_SMART);
|
||
}
|
||
}
|
||
|
||
|
||
intptr_t ScintillaEditView::searchInTarget(QByteArray& text2Find,size_t fromPos, size_t toPos) const
|
||
{
|
||
execute(SCI_SETTARGETRANGE, fromPos, toPos);
|
||
return execute(SCI_SEARCHINTARGET, text2Find.size(), reinterpret_cast<sptr_t>(text2Find.data()));
|
||
}
|
||
|
||
intptr_t ScintillaEditView::replaceTargetRegExMode(QByteArray& re, intptr_t fromTargetPos, intptr_t toTargetPos) const
|
||
{
|
||
if (fromTargetPos != -1 || toTargetPos != -1)
|
||
{
|
||
execute(SCI_SETTARGETRANGE, fromTargetPos, toTargetPos);
|
||
}
|
||
return execute(SCI_REPLACETARGETRE, re.size(), reinterpret_cast<sptr_t>(re.data()));
|
||
}
|
||
|
||
|
||
//替换fromTargetPos 到 toTargetPos的内容为str2replace。
|
||
intptr_t ScintillaEditView::replaceTarget(QByteArray& str2replace, intptr_t fromTargetPos, intptr_t toTargetPos) const
|
||
{
|
||
if (fromTargetPos != -1 || toTargetPos != -1)
|
||
{
|
||
execute(SCI_SETTARGETRANGE, fromTargetPos, toTargetPos);
|
||
}
|
||
|
||
return execute(SCI_REPLACETARGET, str2replace.size(), reinterpret_cast<sptr_t>(str2replace.data()));
|
||
}
|
||
|
||
|
||
void ScintillaEditView::highlightViewWithWord(QString & word2Hilite)
|
||
{
|
||
int originalStartPos = execute(SCI_GETTARGETSTART);
|
||
int originalEndPos = execute(SCI_GETTARGETEND);
|
||
|
||
int firstLine = static_cast<int>(this->execute(SCI_GETFIRSTVISIBLELINE));
|
||
int nbLineOnScreen = this->execute(SCI_LINESONSCREEN);
|
||
int nbLines = std::min(nbLineOnScreen, MAXLINEHIGHLIGHT) + 1;
|
||
int lastLine = firstLine + nbLines;
|
||
int startPos = 0;
|
||
int endPos = 0;
|
||
auto currentLine = firstLine;
|
||
int prevDocLineChecked = -1; //invalid start
|
||
|
||
|
||
auto searchMark = [this](int &startPos, int &endPos, QByteArray &word2Mark){
|
||
|
||
int targetStart = 0;
|
||
int targetEnd = 0;
|
||
|
||
long lens = word2Mark.length();
|
||
|
||
while (targetStart >= 0)
|
||
{
|
||
execute(SCI_SETTARGETRANGE, startPos, endPos);
|
||
|
||
targetStart = SendScintilla(SCI_SEARCHINTARGET, lens, word2Mark.data());
|
||
|
||
if (targetStart == -1 || targetStart == -2)
|
||
break;
|
||
|
||
targetEnd = int(this->execute(SCI_GETTARGETEND));
|
||
|
||
if (targetEnd > endPos)
|
||
{
|
||
//we found a result but outside our range, therefore do not process it
|
||
break;
|
||
}
|
||
|
||
int foundTextLen = targetEnd - targetStart;
|
||
|
||
if (foundTextLen > 0)
|
||
{
|
||
|
||
this->execute(SCI_SETINDICATORCURRENT, SCE_UNIVERSAL_FOUND_STYLE_SMART);
|
||
this->execute(SCI_INDICATORFILLRANGE, targetStart, foundTextLen);
|
||
}
|
||
|
||
if (targetStart + foundTextLen == endPos)
|
||
break;
|
||
|
||
startPos = targetStart + foundTextLen;
|
||
|
||
}
|
||
};
|
||
|
||
|
||
QByteArray whatMark = word2Hilite.toUtf8();
|
||
|
||
SendScintilla(SCI_SETSEARCHFLAGS, SCFIND_REGEXP | SCFIND_MATCHCASE | SCFIND_WHOLEWORD | SCFIND_REGEXP_SKIPCRLFASONE);
|
||
|
||
for (; currentLine < lastLine; ++currentLine)
|
||
{
|
||
int docLine = static_cast<int>(this->execute(SCI_DOCLINEFROMVISIBLE, currentLine));
|
||
if (docLine == prevDocLineChecked)
|
||
continue; //still on same line (wordwrap)
|
||
prevDocLineChecked = docLine;
|
||
startPos = static_cast<int>(this->execute(SCI_POSITIONFROMLINE, docLine));
|
||
endPos = static_cast<int>(this->execute(SCI_POSITIONFROMLINE, docLine + 1));
|
||
|
||
if (endPos == -1)
|
||
{ //past EOF
|
||
endPos = this->length() - 1;
|
||
searchMark(startPos, endPos, whatMark);
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
searchMark(startPos, endPos, whatMark);
|
||
}
|
||
}
|
||
|
||
m_hasHighlight = true;
|
||
|
||
// restore the original targets to avoid conflicts with the search/replace functions
|
||
this->execute(SCI_SETTARGETRANGE, originalStartPos, originalEndPos);
|
||
}
|
||
|
||
void ScintillaEditView::slot_markColorGroup(QAction *action)
|
||
{
|
||
CCNotePad::s_curMarkColorId = action->data().toInt();
|
||
if (m_NoteWin != nullptr)
|
||
{
|
||
m_NoteWin->slot_wordHighlight();
|
||
}
|
||
}
|
||
|
||
//修改标记样式的颜色
|
||
void ScintillaEditView::changeStyleColor(int sytleId)
|
||
{
|
||
if (m_styleColorMenu == nullptr)
|
||
{
|
||
initStyleColorMenu();
|
||
}
|
||
if( sytleId < 5)
|
||
{
|
||
QPixmap colorBar(36, 36);
|
||
colorBar.fill((&StyleSet::s_global_style->mark_style_1)[sytleId].bgColor);
|
||
m_styleMarkActList.at(sytleId)->setIcon(colorBar);
|
||
|
||
if (m_NoteWin != nullptr)
|
||
{
|
||
m_NoteWin->changeMarkColor(sytleId);
|
||
}
|
||
}
|
||
}
|
||
|
||
void ScintillaEditView::initStyleColorMenu()
|
||
{
|
||
if (m_styleColorMenu == nullptr)
|
||
{
|
||
m_styleColorMenu = new QMenu(tr("mark with color"), this);
|
||
QPixmap colorBar(36, 36);
|
||
|
||
QActionGroup* markColorGroup = new QActionGroup(this);
|
||
connect(markColorGroup, &QActionGroup::triggered, this, &ScintillaEditView::slot_markColorGroup, Qt::QueuedConnection);
|
||
|
||
int index = 1;
|
||
auto initColorBar = [this, markColorGroup, &index](QPixmap& colorBar)->QAction* {
|
||
QAction* action = new QAction(m_styleColorMenu);
|
||
action->setIcon(colorBar);
|
||
action->setCheckable(true);
|
||
action->setText(tr("Color %1").arg(index));
|
||
action->setData(index + SCE_UNIVERSAL_FOUND_STYLE_EXT5 - 1);
|
||
++index;
|
||
m_styleColorMenu->addAction(action);
|
||
markColorGroup->addAction(action);
|
||
return action;
|
||
};
|
||
|
||
colorBar.fill(StyleSet::s_global_style->mark_style_1.bgColor);
|
||
m_styleMarkActList.append(initColorBar(colorBar));
|
||
|
||
colorBar.fill(StyleSet::s_global_style->mark_style_2.bgColor);
|
||
m_styleMarkActList.append(initColorBar(colorBar));
|
||
|
||
colorBar.fill(StyleSet::s_global_style->mark_style_3.bgColor);
|
||
m_styleMarkActList.append(initColorBar(colorBar));
|
||
|
||
colorBar.fill(StyleSet::s_global_style->mark_style_4.bgColor);
|
||
m_styleMarkActList.append(initColorBar(colorBar));
|
||
|
||
colorBar.fill(StyleSet::s_global_style->mark_style_5.bgColor);
|
||
m_styleMarkActList.append(initColorBar(colorBar));
|
||
|
||
|
||
m_styleColorMenu->addSeparator();
|
||
|
||
if (m_NoteWin != nullptr)
|
||
{
|
||
m_styleColorMenu->addAction(tr("Clear Select"), m_NoteWin, &CCNotePad::slot_clearWordHighlight);
|
||
m_styleColorMenu->addAction(tr("Clear All"), m_NoteWin, &CCNotePad::slot_clearMark);
|
||
}
|
||
}
|
||
}
|
||
|
||
void ScintillaEditView::contextUserDefineMenuEvent(QMenu* menu)
|
||
{
|
||
//QAction* action;
|
||
if (menu != nullptr && (m_NoteWin !=nullptr))
|
||
{
|
||
menu->addAction(tr("Show File in Explorer"), m_NoteWin, &CCNotePad::slot_showFileInExplorer);
|
||
|
||
if (m_styleColorMenu == nullptr)
|
||
{
|
||
m_styleColorMenu = new QMenu(tr("mark with color"),this);
|
||
QPixmap colorBar(36, 36);
|
||
|
||
QActionGroup* markColorGroup = new QActionGroup(this);
|
||
connect(markColorGroup, &QActionGroup::triggered, this, &ScintillaEditView::slot_markColorGroup, Qt::QueuedConnection);
|
||
|
||
int index = 1;
|
||
auto initColorBar = [this, markColorGroup, &index](QPixmap& colorBar)->QAction* {
|
||
QAction* action = new QAction(m_styleColorMenu);
|
||
action->setIcon(colorBar);
|
||
action->setCheckable(true);
|
||
action->setText(tr("Color %1").arg(index));
|
||
action->setData(index + SCE_UNIVERSAL_FOUND_STYLE_EXT5-1);
|
||
++index;
|
||
m_styleColorMenu->addAction(action);
|
||
markColorGroup->addAction(action);
|
||
return action;
|
||
};
|
||
|
||
colorBar.fill(StyleSet::s_global_style->mark_style_1.fgColor);
|
||
m_styleMarkActList.append(initColorBar(colorBar));
|
||
|
||
colorBar.fill(StyleSet::s_global_style->mark_style_2.fgColor);
|
||
m_styleMarkActList.append(initColorBar(colorBar));
|
||
|
||
colorBar.fill(StyleSet::s_global_style->mark_style_3.fgColor);
|
||
m_styleMarkActList.append(initColorBar(colorBar));
|
||
|
||
colorBar.fill(StyleSet::s_global_style->mark_style_4.fgColor);
|
||
m_styleMarkActList.append(initColorBar(colorBar));
|
||
|
||
colorBar.fill(StyleSet::s_global_style->mark_style_5.fgColor);
|
||
m_styleMarkActList.append(initColorBar(colorBar));
|
||
|
||
|
||
m_styleColorMenu->addSeparator();
|
||
|
||
m_styleColorMenu->addAction(tr("Clear Select"), m_NoteWin, &CCNotePad::slot_clearWordHighlight);
|
||
m_styleColorMenu->addAction(tr("Clear All"), m_NoteWin, &CCNotePad::slot_clearMark);
|
||
}
|
||
menu->addMenu(m_styleColorMenu);
|
||
|
||
menu->addSeparator();
|
||
|
||
menu->addAction(tr("Add/Del line comment"), [this]() {
|
||
doBlockComment(cm_toggle);
|
||
});
|
||
menu->addAction(tr("Add Block comment"), [this]() {
|
||
doStreamComment();
|
||
});
|
||
menu->addAction(tr("Del Block comment"), [this]() {
|
||
undoStreamComment();
|
||
});
|
||
|
||
menu->addSeparator();
|
||
menu->addAction(tr("Word Count"), [this]() {
|
||
showWordNums();
|
||
});
|
||
|
||
}
|
||
menu->show();
|
||
}
|
||
|
||
void ScintillaEditView::replaceSelWith(const char* replaceText)
|
||
{
|
||
execute(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(replaceText));
|
||
}
|
||
|
||
bool ScintillaEditView::doBlockComment(Comment_Mode currCommentMode)
|
||
{
|
||
if (this->isReadOnly())
|
||
{
|
||
return false;
|
||
}
|
||
|
||
//-- BlockToStreamComment:
|
||
QByteArray commentStart;
|
||
QByteArray commentEnd;
|
||
|
||
QByteArray symbolStart;
|
||
QByteArray symbolEnd;
|
||
|
||
QByteArray commentLineSymbol;
|
||
QByteArray symbol;
|
||
|
||
|
||
bool isSingleLineAdvancedMode = false;
|
||
QsciLexer* pLexer = this->lexer();
|
||
|
||
if (pLexer == nullptr)
|
||
{
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
commentLineSymbol = pLexer->getCommentLineSymbol();
|
||
commentStart = pLexer->getCommentStart();
|
||
commentEnd = pLexer->getCommentEnd();
|
||
}
|
||
|
||
if ((commentLineSymbol.isEmpty()) || commentLineSymbol.isEmpty())
|
||
{
|
||
// BlockToStreamComment: If there is no block-comment symbol, try the stream comment:
|
||
if (!(commentStart.isEmpty() || commentEnd.isEmpty()))
|
||
{
|
||
if (currCommentMode == cm_comment)
|
||
{
|
||
isSingleLineAdvancedMode = true;
|
||
|
||
}
|
||
else if (currCommentMode == cm_uncomment)
|
||
{
|
||
return undoStreamComment(false);
|
||
}
|
||
else if (currCommentMode == cm_toggle)
|
||
{
|
||
isSingleLineAdvancedMode = true;
|
||
}
|
||
else
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
//For Single Line NORMAL MODE
|
||
QByteArray comment;
|
||
size_t comment_length = 0;
|
||
|
||
//For Single Line ADVANCED MODE
|
||
QByteArray advCommentStart;
|
||
QByteArray advCommentEnd;
|
||
size_t advCommentStart_length = 0;
|
||
size_t advCommentEnd_length = 0;
|
||
|
||
QByteArray aSpace(" ");
|
||
|
||
//Only values that have passed through will be assigned, to be sure they are valid!
|
||
if (!isSingleLineAdvancedMode)
|
||
{
|
||
comment = commentLineSymbol;
|
||
|
||
if (!(pLexer->lexerId() == L_BAANC)) // BaanC standardization - no space.
|
||
{
|
||
comment += aSpace;
|
||
}
|
||
|
||
comment_length = comment.length();
|
||
}
|
||
else // isSingleLineAdvancedMode
|
||
{
|
||
advCommentStart = commentStart;
|
||
advCommentStart += aSpace;
|
||
advCommentEnd = aSpace;
|
||
advCommentEnd += commentEnd;
|
||
|
||
advCommentStart_length = advCommentStart.length();
|
||
advCommentEnd_length = advCommentEnd.length();
|
||
}
|
||
|
||
size_t selectionStart = this->execute(SCI_GETSELECTIONSTART);
|
||
size_t selectionEnd = this->execute(SCI_GETSELECTIONEND);
|
||
size_t caretPosition = this->execute(SCI_GETCURRENTPOS);
|
||
|
||
bool move_caret = caretPosition < selectionEnd;
|
||
intptr_t selStartLine = this->execute(SCI_LINEFROMPOSITION, selectionStart);
|
||
intptr_t selEndLine = this->execute(SCI_LINEFROMPOSITION, selectionEnd);
|
||
intptr_t lines = selEndLine - selStartLine;
|
||
|
||
if ((lines > 0) && (selectionEnd == static_cast<size_t>(this->execute(SCI_POSITIONFROMLINE, selEndLine))))
|
||
{
|
||
selEndLine--;
|
||
}
|
||
|
||
// count lines which were un-commented to decide if undoStreamComment() shall be called.
|
||
int nUncomments = 0;
|
||
//Some Lexers need line-comments at the beginning of a line.
|
||
const bool avoidIndent = (pLexer->lexerId() == L_FORTRAN_77 || pLexer->lexerId() == L_BAANC);
|
||
//Some Lexers comment blank lines, per their standards.
|
||
const bool commentEmptyLines = (pLexer->lexerId() == L_BAANC);
|
||
|
||
this->execute(SCI_BEGINUNDOACTION);
|
||
|
||
for (intptr_t i = selStartLine; i <= selEndLine; ++i)
|
||
{
|
||
size_t lineStart = this->execute(SCI_POSITIONFROMLINE, i);
|
||
size_t lineIndent = this->execute(SCI_GETLINEINDENTPOSITION, i);
|
||
size_t lineEnd = this->execute(SCI_GETLINEENDPOSITION, i);
|
||
|
||
// empty lines are not commented, unless required by the language.
|
||
if (lineIndent == lineEnd && !commentEmptyLines)
|
||
continue;
|
||
|
||
if (avoidIndent)
|
||
lineIndent = lineStart;
|
||
|
||
//这里linebufferSize不需要包含字符串后面的\0,所以不需要预留1个\0符号
|
||
size_t linebufferSize = lineEnd - lineIndent;
|
||
|
||
QByteArray linebuf;
|
||
linebuf.resize(linebufferSize);
|
||
this->getText(linebuf.data(), lineIndent, lineEnd);
|
||
|
||
QByteArray linebufStr = linebuf;
|
||
|
||
if (currCommentMode != cm_comment) // uncomment/toggle
|
||
{
|
||
if (!isSingleLineAdvancedMode)
|
||
{
|
||
if (qstrncmp(linebufStr.data(), comment.data(), !(pLexer->lexerId() == L_BAANC) ? comment_length - 1 : comment_length) == 0)
|
||
{
|
||
size_t len = (linebufStr[(int)comment_length - 1] == aSpace[0]) ? comment_length : (!(pLexer->lexerId() == L_BAANC) ? comment_length - 1 : comment_length);
|
||
|
||
this->execute(SCI_SETSEL, lineIndent, lineIndent + len);
|
||
this->replaceSelWith("");
|
||
|
||
// SELECTION RANGE ADJUSTMENT .......................
|
||
if (i == selStartLine) // first selected line
|
||
{
|
||
if (selectionStart > lineIndent + len)
|
||
selectionStart -= len;
|
||
else if (selectionStart > lineIndent)
|
||
selectionStart = lineIndent;
|
||
} // ................................................
|
||
if (i == selEndLine) // last selected line
|
||
{
|
||
if (selectionEnd > lineIndent + len)
|
||
selectionEnd -= len;
|
||
else if (selectionEnd > lineIndent)
|
||
{
|
||
selectionEnd = lineIndent;
|
||
if (lineIndent == lineStart && i != selStartLine)
|
||
++selectionEnd; // avoid caret return in this case
|
||
}
|
||
} // ................................................
|
||
else // every iteration except the last selected line
|
||
selectionEnd -= len;
|
||
// ..................................................
|
||
|
||
++nUncomments;
|
||
continue;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if ((qstrncmp(linebufStr.data(), advCommentStart.data(), advCommentStart_length - 1) == 0) &&
|
||
(qstrncmp(linebufStr.mid(linebufStr.length() - advCommentEnd_length + 1, advCommentEnd_length - 1).data(), advCommentEnd.mid(1, advCommentEnd_length - 1).data(), advCommentEnd_length - 1) == 0))
|
||
{
|
||
size_t startLen = linebufStr[(int)advCommentStart_length - 1] == aSpace[0] ? advCommentStart_length : advCommentStart_length - 1;
|
||
size_t endLen = linebufStr[int(linebufStr.length() - advCommentEnd_length)] == aSpace[0] ? advCommentEnd_length : advCommentEnd_length - 1;
|
||
|
||
this->execute(SCI_SETSEL, lineIndent, lineIndent + startLen);
|
||
this->replaceSelWith("");
|
||
this->execute(SCI_SETSEL, lineEnd - startLen - endLen, lineEnd - startLen);
|
||
this->replaceSelWith("");
|
||
|
||
// SELECTION RANGE ADJUSTMENT .......................
|
||
if (i == selStartLine) // first selected line
|
||
{
|
||
if (selectionStart > lineEnd - endLen)
|
||
selectionStart = lineEnd - startLen - endLen;
|
||
else if (selectionStart > lineIndent + startLen)
|
||
selectionStart -= startLen;
|
||
else if (selectionStart > lineIndent)
|
||
selectionStart = lineIndent;
|
||
} // ................................................
|
||
if (i == selEndLine) // last selected line
|
||
{
|
||
if (selectionEnd > lineEnd)
|
||
selectionEnd -= (startLen + endLen);
|
||
else if (selectionEnd > lineEnd - endLen)
|
||
selectionEnd = lineEnd - startLen - endLen;
|
||
else if (selectionEnd > lineIndent + startLen)
|
||
selectionEnd -= startLen;
|
||
else if (selectionEnd > lineIndent)
|
||
{
|
||
selectionEnd = lineIndent;
|
||
if (lineIndent == lineStart && i != selStartLine)
|
||
++selectionEnd; // avoid caret return in this case
|
||
}
|
||
} // ................................................
|
||
else // every iteration except the last selected line
|
||
selectionEnd -= (startLen + endLen);
|
||
// ..................................................
|
||
|
||
++nUncomments;
|
||
continue;
|
||
}
|
||
}
|
||
} // uncomment/toggle
|
||
|
||
if (currCommentMode != cm_uncomment) // comment/toggle
|
||
{
|
||
if (!isSingleLineAdvancedMode)
|
||
{
|
||
this->insertCharsFrom(lineIndent, comment);
|
||
|
||
// SELECTION RANGE ADJUSTMENT .......................
|
||
if (i == selStartLine) // first selected line
|
||
{
|
||
if (selectionStart >= lineIndent)
|
||
selectionStart += comment_length;
|
||
} // ................................................
|
||
if (i == selEndLine) // last selected line
|
||
{
|
||
if (selectionEnd >= lineIndent)
|
||
selectionEnd += comment_length;
|
||
} // ................................................
|
||
else // every iteration except the last selected line
|
||
selectionEnd += comment_length;
|
||
// ..................................................
|
||
}
|
||
else // isSingleLineAdvancedMode
|
||
{
|
||
this->insertCharsFrom(lineIndent, advCommentStart);
|
||
this->insertCharsFrom(lineEnd + advCommentStart_length, advCommentEnd);
|
||
|
||
// SELECTION RANGE ADJUSTMENT .......................
|
||
if (i == selStartLine) // first selected line
|
||
{
|
||
if (selectionStart >= lineIndent)
|
||
selectionStart += advCommentStart_length;
|
||
} // ................................................
|
||
if (i == selEndLine) // last selected line
|
||
{
|
||
if (selectionEnd > lineEnd)
|
||
selectionEnd += (advCommentStart_length + advCommentEnd_length);
|
||
else if (selectionEnd >= lineIndent)
|
||
selectionEnd += advCommentStart_length;
|
||
} // ................................................
|
||
else // every iteration except the last selected line
|
||
selectionEnd += (advCommentStart_length + advCommentEnd_length);
|
||
// ..................................................
|
||
}
|
||
} // comment/toggle
|
||
} // for (...)
|
||
|
||
if (move_caret)
|
||
{
|
||
// moving caret to the beginning of selected block
|
||
this->execute(SCI_GOTOPOS, selectionEnd);
|
||
this->execute(SCI_SETCURRENTPOS, selectionStart);
|
||
}
|
||
else
|
||
{
|
||
this->execute(SCI_SETSEL, selectionStart, selectionEnd);
|
||
}
|
||
this->execute(SCI_ENDUNDOACTION);
|
||
|
||
// undoStreamComment: If there were no block-comments to un-comment try uncommenting of stream-comment.
|
||
if ((currCommentMode == cm_uncomment) && (nUncomments == 0))
|
||
{
|
||
return undoStreamComment(false);
|
||
}
|
||
return true;
|
||
}
|
||
|
||
//显示文字的字数
|
||
void ScintillaEditView::showWordNums()
|
||
{
|
||
if (hasSelectedText())
|
||
{
|
||
QString word = selectedText();
|
||
if (!word.isEmpty())
|
||
{
|
||
QMessageBox::about(this, tr("Word Nums"), tr("Current Select Word Nums is %1 .").arg(word.size()));
|
||
}
|
||
}
|
||
else
|
||
{
|
||
QMessageBox::about(this, tr("Word Nums"), tr("Current Doc Word Nums is %1 .").arg(this->text().size()));
|
||
}
|
||
|
||
}
|
||
|
||
bool ScintillaEditView::undoStreamComment(bool tryBlockComment)
|
||
{
|
||
QByteArray commentStart;
|
||
QByteArray commentEnd;
|
||
QByteArray commentLineSymbol;
|
||
|
||
QByteArray symbolStart;
|
||
QByteArray symbolEnd;
|
||
QByteArray symbol;
|
||
|
||
const int charbufLen = 10;
|
||
QByteArray charbuf;
|
||
charbuf.reserve(charbufLen);
|
||
|
||
bool retVal = false;
|
||
|
||
QsciLexer* pLexer = this->lexer();
|
||
//-- Avoid side-effects (e.g. cursor moves number of comment-characters) when file is read-only.
|
||
if (this->isReadOnly())
|
||
return false;
|
||
|
||
if (pLexer->lexer() == nullptr)
|
||
{
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
commentLineSymbol = pLexer->getCommentLineSymbol();
|
||
commentStart = pLexer->getCommentStart();
|
||
commentEnd = pLexer->getCommentEnd();
|
||
}
|
||
|
||
|
||
// BlockToStreamComment: If there is no stream-comment symbol and we came not from doBlockComment, try the block comment:
|
||
if (commentStart.isEmpty() || commentEnd.isEmpty())
|
||
{
|
||
if (!commentLineSymbol.isEmpty() && tryBlockComment)
|
||
return doBlockComment(cm_uncomment);
|
||
else
|
||
return false;
|
||
}
|
||
|
||
QByteArray start_comment(commentStart);
|
||
QByteArray end_comment(commentEnd);
|
||
QByteArray white_space(" ");
|
||
size_t start_comment_length = start_comment.length();
|
||
size_t end_comment_length = end_comment.length();
|
||
|
||
// do as long as stream-comments are within selection
|
||
do
|
||
{
|
||
auto selectionStart = this->execute(SCI_GETSELECTIONSTART);
|
||
auto selectionEnd = this->execute(SCI_GETSELECTIONEND);
|
||
auto caretPosition = this->execute(SCI_GETCURRENTPOS);
|
||
auto docLength = this->execute(SCI_GETLENGTH);
|
||
|
||
// checking if caret is located in _beginning_ of selected block
|
||
bool move_caret = caretPosition < selectionEnd;
|
||
|
||
//-- Note: The caretPosition is either at selectionEnd or at selectionStart!! selectionStart is always before (smaller) than selectionEnd!!
|
||
|
||
//-- First, search all start_comment and end_comment before and after the selectionStart and selectionEnd position.
|
||
const int iSelStart = 0, iSelEnd = 1;
|
||
const size_t N_CMNT = 2;
|
||
intptr_t posStartCommentBefore[N_CMNT], posEndCommentBefore[N_CMNT], posStartCommentAfter[N_CMNT], posEndCommentAfter[N_CMNT];
|
||
bool blnStartCommentBefore[N_CMNT], blnEndCommentBefore[N_CMNT], blnStartCommentAfter[N_CMNT], blnEndCommentAfter[N_CMNT];
|
||
intptr_t posStartComment, posEndComment;
|
||
intptr_t selectionStartMove, selectionEndMove;
|
||
int flags;
|
||
|
||
//-- Directly use Scintilla-Functions
|
||
// rather than _findReplaceDlg.processFindNext()which does not return the find-position and is not quiet!
|
||
flags = SCFIND_WORDSTART;
|
||
this->execute(SCI_SETSEARCHFLAGS, flags);
|
||
//-- Find all start- and end-comments before and after the selectionStart position.
|
||
//-- When searching upwards the start-position for searching must be moved one after the current position
|
||
// to find a search-string just starting before the current position!
|
||
//-- Direction DIR_UP ---
|
||
posStartCommentBefore[iSelStart] = this->searchInTarget(start_comment, selectionStart, 0);
|
||
(posStartCommentBefore[iSelStart] == -1 ? blnStartCommentBefore[iSelStart] = false : blnStartCommentBefore[iSelStart] = true);
|
||
posEndCommentBefore[iSelStart] = this->searchInTarget(end_comment, selectionStart, 0);
|
||
(posEndCommentBefore[iSelStart] == -1 ? blnEndCommentBefore[iSelStart] = false : blnEndCommentBefore[iSelStart] = true);
|
||
//-- Direction DIR_DOWN ---
|
||
posStartCommentAfter[iSelStart] = this->searchInTarget(start_comment, selectionStart, docLength);
|
||
(posStartCommentAfter[iSelStart] == -1 ? blnStartCommentAfter[iSelStart] = false : blnStartCommentAfter[iSelStart] = true);
|
||
posEndCommentAfter[iSelStart] = this->searchInTarget(end_comment, selectionStart, docLength);
|
||
(posEndCommentAfter[iSelStart] == -1 ? blnEndCommentAfter[iSelStart] = false : blnEndCommentAfter[iSelStart] = true);
|
||
|
||
//-- Check, if selectionStart or selectionEnd is within a stream comment -----
|
||
// or if the selection includes a complete stream-comment!! ----------------
|
||
|
||
//-- First, check if there is a stream-comment around the selectionStart position:
|
||
if ((blnStartCommentBefore[iSelStart] && blnEndCommentAfter[iSelStart])
|
||
&& (!blnEndCommentBefore[iSelStart] || (posStartCommentBefore[iSelStart] >= posEndCommentBefore[iSelStart]))
|
||
&& (!blnStartCommentAfter[iSelStart] || (posEndCommentAfter[iSelStart] <= posStartCommentAfter[iSelStart])))
|
||
{
|
||
posStartComment = posStartCommentBefore[iSelStart];
|
||
posEndComment = posEndCommentAfter[iSelStart];
|
||
}
|
||
else //-- Second, check if there is a stream-comment around the selectionEnd position:
|
||
{
|
||
//-- Find all start- and end-comments before and after the selectionEnd position.
|
||
//-- Direction DIR_UP ---
|
||
posStartCommentBefore[iSelEnd] = this->searchInTarget(start_comment, selectionEnd, 0);
|
||
(posStartCommentBefore[iSelEnd] == -1 ? blnStartCommentBefore[iSelEnd] = false : blnStartCommentBefore[iSelEnd] = true);
|
||
posEndCommentBefore[iSelEnd] = this->searchInTarget(end_comment, selectionEnd, 0);
|
||
(posEndCommentBefore[iSelEnd] == -1 ? blnEndCommentBefore[iSelEnd] = false : blnEndCommentBefore[iSelEnd] = true);
|
||
//-- Direction DIR_DOWN ---
|
||
posStartCommentAfter[iSelEnd] = this->searchInTarget(start_comment, selectionEnd, docLength);
|
||
(posStartCommentAfter[iSelEnd] == -1 ? blnStartCommentAfter[iSelEnd] = false : blnStartCommentAfter[iSelEnd] = true);
|
||
posEndCommentAfter[iSelEnd] = this->searchInTarget(end_comment, selectionEnd, docLength);
|
||
(posEndCommentAfter[iSelEnd] == -1 ? blnEndCommentAfter[iSelEnd] = false : blnEndCommentAfter[iSelEnd] = true);
|
||
|
||
if ((blnStartCommentBefore[iSelEnd] && blnEndCommentAfter[iSelEnd])
|
||
&& (!blnEndCommentBefore[iSelEnd] || (posStartCommentBefore[iSelEnd] >= posEndCommentBefore[iSelEnd]))
|
||
&& (!blnStartCommentAfter[iSelEnd] || (posEndCommentAfter[iSelEnd] <= posStartCommentAfter[iSelEnd])))
|
||
{
|
||
posStartComment = posStartCommentBefore[iSelEnd];
|
||
posEndComment = posEndCommentAfter[iSelEnd];
|
||
}
|
||
//-- Third, check if there is a stream-comment within the selected area:
|
||
else if ((blnStartCommentAfter[iSelStart] && (posStartCommentAfter[iSelStart] < selectionEnd))
|
||
&& (blnEndCommentBefore[iSelEnd] && (posEndCommentBefore[iSelEnd] > selectionStart)))
|
||
{
|
||
//-- If there are more than one stream-comment within the selection, take the first one after selectionStart!!
|
||
posStartComment = posStartCommentAfter[iSelStart];
|
||
posEndComment = posEndCommentAfter[iSelStart];
|
||
}
|
||
//-- Finally, if there is no stream-comment, return
|
||
else
|
||
return retVal;
|
||
}
|
||
|
||
//-- Ok, there are valid start-comment and valid end-comment around the caret-position.
|
||
// Now, un-comment stream-comment:
|
||
retVal = true;
|
||
intptr_t startCommentLength = start_comment_length;
|
||
intptr_t endCommentLength = end_comment_length;
|
||
|
||
//-- First delete end-comment, so that posStartCommentBefore does not change!
|
||
//-- Get character before end-comment to decide, if there is a white character before the end-comment, which will be removed too!
|
||
this->getText(charbuf.data(), posEndComment - 1, posEndComment);
|
||
if (qstrncmp(charbuf, white_space, white_space.length()) == 0)
|
||
{
|
||
endCommentLength += 1;
|
||
posEndComment -= 1;
|
||
}
|
||
//-- Delete end stream-comment string ---------
|
||
this->execute(SCI_BEGINUNDOACTION);
|
||
this->execute(SCI_SETSEL, posEndComment, posEndComment + endCommentLength);
|
||
this->execute(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(""));
|
||
|
||
//-- Get character after start-comment to decide, if there is a white character after the start-comment, which will be removed too!
|
||
this->getText(charbuf.data(), posStartComment + startCommentLength, posStartComment + startCommentLength + 1);
|
||
if (qstrncmp(charbuf, white_space, white_space.length()) == 0)
|
||
startCommentLength += 1;
|
||
|
||
//-- Delete starting stream-comment string ---------
|
||
this->execute(SCI_SETSEL, posStartComment, posStartComment + startCommentLength);
|
||
this->execute(SCI_REPLACESEL, 0, reinterpret_cast<sptr_t>(""));
|
||
this->execute(SCI_ENDUNDOACTION);
|
||
|
||
//-- Reset selection before calling the routine
|
||
//-- Determine selection movement
|
||
// selectionStart
|
||
if (selectionStart > posStartComment)
|
||
{
|
||
if (selectionStart >= posStartComment + startCommentLength)
|
||
selectionStartMove = -startCommentLength;
|
||
else
|
||
selectionStartMove = -selectionStart - posStartComment;
|
||
}
|
||
else
|
||
selectionStartMove = 0;
|
||
|
||
// selectionEnd
|
||
if (selectionEnd >= posEndComment + endCommentLength)
|
||
selectionEndMove = -startCommentLength + endCommentLength;
|
||
else if (selectionEnd <= posEndComment)
|
||
selectionEndMove = -startCommentLength;
|
||
else
|
||
selectionEndMove = -startCommentLength + (selectionEnd - posEndComment);
|
||
|
||
//-- Reset selection of text without deleted stream-comment-string
|
||
if (move_caret)
|
||
{
|
||
// moving caret to the beginning of selected block
|
||
this->execute(SCI_GOTOPOS, selectionEnd + selectionEndMove);
|
||
this->execute(SCI_SETCURRENTPOS, selectionStart + selectionStartMove);
|
||
}
|
||
else
|
||
{
|
||
this->execute(SCI_SETSEL, selectionStart + selectionStartMove, selectionEnd + selectionEndMove);
|
||
}
|
||
} while (1); //do as long as stream-comments are within selection
|
||
}
|
||
|
||
bool ScintillaEditView::doStreamComment()
|
||
{
|
||
QByteArray commentStart;
|
||
QByteArray commentEnd;
|
||
|
||
QByteArray symbolStart;
|
||
QByteArray symbolEnd;
|
||
|
||
// BlockToStreamComment:
|
||
QByteArray commentLineSymbol;
|
||
QByteArray symbol;
|
||
|
||
|
||
if (this->isReadOnly())
|
||
return false;
|
||
|
||
QsciLexer* pLexer = this->lexer();
|
||
|
||
if (pLexer == nullptr)
|
||
{
|
||
return false;
|
||
}
|
||
else
|
||
{
|
||
// BlockToStreamComment: Next line needed to decide, if block-comment can be called below!
|
||
commentLineSymbol = pLexer->getCommentLineSymbol();
|
||
commentStart = pLexer->getCommentStart();
|
||
commentEnd = pLexer->getCommentEnd();
|
||
}
|
||
|
||
// BlockToStreamComment: If there is no stream-comment symbol, try the block comment:
|
||
if (commentStart.isEmpty()||commentEnd.isEmpty())
|
||
{
|
||
if (!commentLineSymbol.isEmpty())
|
||
return doBlockComment(cm_comment);
|
||
else
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
QByteArray start_comment(commentStart);
|
||
QByteArray end_comment(commentEnd);
|
||
QByteArray white_space(" ");
|
||
|
||
start_comment += white_space;
|
||
white_space += end_comment;
|
||
end_comment = white_space;
|
||
size_t start_comment_length = start_comment.length();
|
||
size_t selectionStart = this->execute(SCI_GETSELECTIONSTART);
|
||
size_t selectionEnd = this->execute(SCI_GETSELECTIONEND);
|
||
size_t caretPosition = this->execute(SCI_GETCURRENTPOS);
|
||
// checking if caret is located in _beginning_ of selected block
|
||
bool move_caret = caretPosition < selectionEnd;
|
||
|
||
// if there is no selection?
|
||
if (selectionEnd - selectionStart <= 0)
|
||
{
|
||
auto selLine = this->execute(SCI_LINEFROMPOSITION, selectionStart);
|
||
selectionStart = this->execute(SCI_GETLINEINDENTPOSITION, selLine);
|
||
selectionEnd = this->execute(SCI_GETLINEENDPOSITION, selLine);
|
||
}
|
||
this->execute(SCI_BEGINUNDOACTION);
|
||
this->insertCharsFrom(selectionStart, start_comment);
|
||
selectionEnd += start_comment_length;
|
||
selectionStart += start_comment_length;
|
||
this->insertCharsFrom(selectionEnd, end_comment);
|
||
if (move_caret)
|
||
{
|
||
// moving caret to the beginning of selected block
|
||
this->execute(SCI_GOTOPOS, selectionEnd);
|
||
this->execute(SCI_SETCURRENTPOS, selectionStart);
|
||
}
|
||
else
|
||
{
|
||
this->execute(SCI_SETSEL, selectionStart, selectionEnd);
|
||
}
|
||
this->execute(SCI_ENDUNDOACTION);
|
||
return true;
|
||
}
|
||
|
||
void ScintillaEditView::slot_delayWork()
|
||
{
|
||
if (!hasSelectedText())
|
||
{
|
||
return;
|
||
}
|
||
|
||
QString word = selectedText();
|
||
if (!word.isEmpty())
|
||
{
|
||
highlightViewWithWord(word);
|
||
}
|
||
}
|
||
|
||
void ScintillaEditView::dragEnterEvent(QDragEnterEvent* event)
|
||
{
|
||
event->accept();
|
||
}
|
||
|
||
void ScintillaEditView::dropEvent(QDropEvent* e)
|
||
{
|
||
QList<QUrl> urls = e->mimeData()->urls();
|
||
if (!urls.isEmpty())
|
||
{
|
||
|
||
CCNotePad* pv = dynamic_cast<CCNotePad*>(m_NoteWin);
|
||
if (pv != nullptr)
|
||
{
|
||
pv->receiveEditDrop(e);
|
||
}
|
||
return;
|
||
}
|
||
QsciScintilla::dropEvent(e);
|
||
}
|
||
|
||
|
||
|
||
void ScintillaEditView::mouseDoubleClickEvent(QMouseEvent * e)
|
||
{
|
||
if (CCNotePad::s_hightWebAddr == 1)
|
||
{
|
||
do {
|
||
int position = this->execute(SCI_GETCURRENTPOS);
|
||
|
||
auto indicMsk = this->execute(SCI_INDICATORALLONFOR, position);
|
||
if (!(indicMsk & (1 << URL_INDIC)))
|
||
break;
|
||
|
||
auto startPos = this->execute(SCI_INDICATORSTART, URL_INDIC, position);
|
||
auto endPos = this->execute(SCI_INDICATOREND, URL_INDIC, position);
|
||
|
||
if ((position < startPos) || (position > endPos))
|
||
break;
|
||
|
||
// WM_LBUTTONUP goes to opening browser instead of Scintilla here, because the mouse is not captured.
|
||
// The missing message causes mouse cursor flicker as soon as the mouse cursor is moved to a position outside the text editing area.
|
||
//::PostMessage(this->getHSelf(), WM_LBUTTONUP, 0, 0);
|
||
|
||
// Revert selection of current word. Best to this early, otherwise the
|
||
// selected word is visible all the time while the browser is starting
|
||
execute(SCI_SETSEL, position, position);
|
||
|
||
// Open URL
|
||
QByteArray url;
|
||
url.resize(endPos - startPos);
|
||
this->getText(url.data(), static_cast<size_t>(startPos), static_cast<size_t>(endPos));
|
||
QUrl urlweb(url);
|
||
if (urlweb.isValid())
|
||
{
|
||
QDesktopServices::openUrl(urlweb);
|
||
return;
|
||
}
|
||
} while (0);
|
||
}
|
||
|
||
QsciScintilla::mouseDoubleClickEvent(e);
|
||
|
||
if (hasSelectedText())
|
||
{
|
||
emit delayWork();
|
||
}
|
||
}
|
||
|
||
void ScintillaEditView::changeCase(const TextCaseType & caseToConvert, QString& strToConvert) const
|
||
{
|
||
if (strToConvert.isEmpty())
|
||
return;
|
||
|
||
int nbChars = strToConvert.size();
|
||
|
||
switch (caseToConvert)
|
||
{
|
||
case UPPERCASE:
|
||
{
|
||
strToConvert = strToConvert.toUpper();
|
||
break;
|
||
}
|
||
case LOWERCASE:
|
||
{
|
||
strToConvert = strToConvert.toLower();
|
||
break;
|
||
}
|
||
case TITLECASE_FORCE:
|
||
case TITLECASE_BLEND:
|
||
{
|
||
for (int i = 0; i < nbChars; ++i)
|
||
{
|
||
if (strToConvert[i].isLetter())
|
||
{
|
||
if ((i < 1) ? true : !strToConvert[i - 1].isLetterOrNumber())
|
||
strToConvert[i] = strToConvert[i].toUpper();
|
||
else if (caseToConvert == TITLECASE_FORCE)
|
||
strToConvert[i] = strToConvert[i].toLower();
|
||
//An exception
|
||
if ((i < 2) ? false : (strToConvert[i - 1] == L'\'' && (strToConvert[i - 2].isLetter())))
|
||
strToConvert[i] = strToConvert[i].toLower();
|
||
}
|
||
}
|
||
break;
|
||
} //case TITLECASE
|
||
case SENTENCECASE_FORCE:
|
||
case SENTENCECASE_BLEND:
|
||
{
|
||
bool isNewSentence = true;
|
||
bool wasEolR = false;
|
||
bool wasEolN = false;
|
||
for (int i = 0; i < nbChars; ++i)
|
||
{
|
||
if (strToConvert[i].isLetter())
|
||
{
|
||
if (isNewSentence)
|
||
{
|
||
strToConvert[i] = strToConvert[i].toUpper();
|
||
isNewSentence = false;
|
||
}
|
||
else if (caseToConvert == SENTENCECASE_FORCE)
|
||
{
|
||
strToConvert[i] = strToConvert[i].toLower();
|
||
}
|
||
wasEolR = false;
|
||
wasEolN = false;
|
||
//An exception
|
||
if (strToConvert[i] == L'i' &&
|
||
((i < 1) ? false : (strToConvert[i - 1].isSpace() || strToConvert[i - 1] == '(' || strToConvert[i - 1] == '"')) && \
|
||
((i + 1 == nbChars) ? false : (strToConvert[i + 1].isSpace() || strToConvert[i + 1] == '\'')))
|
||
{
|
||
strToConvert[i] = L'I';
|
||
}
|
||
}
|
||
else if (strToConvert[i] == '.' || strToConvert[i] == '!' || strToConvert[i] == '?')
|
||
{
|
||
if ((i + 1 == nbChars) ? true : strToConvert[i + 1].isLetterOrNumber())
|
||
isNewSentence = false;
|
||
else
|
||
isNewSentence = true;
|
||
}
|
||
else if (strToConvert[i] == '\r')
|
||
{
|
||
if (wasEolR)
|
||
isNewSentence = true;
|
||
else
|
||
wasEolR = true;
|
||
}
|
||
else if (strToConvert[i] == '\n')
|
||
{
|
||
if (wasEolN)
|
||
isNewSentence = true;
|
||
else
|
||
wasEolN = true;
|
||
}
|
||
}
|
||
break;
|
||
} //case SENTENCECASE
|
||
case INVERTCASE:
|
||
{
|
||
for (int i = 0; i < nbChars; ++i)
|
||
{
|
||
if (strToConvert[i].isLower())
|
||
strToConvert[i] = strToConvert[i].toUpper();
|
||
else
|
||
strToConvert[i] = strToConvert[i].toLower();
|
||
}
|
||
break;
|
||
} //case INVERTCASE
|
||
case RANDOMCASE:
|
||
{
|
||
for (int i = 0; i < nbChars; ++i)
|
||
{
|
||
if (strToConvert[i].isLetter())
|
||
{
|
||
if (std::rand() & true)
|
||
strToConvert[i] = strToConvert[i].toUpper();
|
||
else
|
||
strToConvert[i] = strToConvert[i].toLower();
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
} //switch (caseToConvert)
|
||
}
|
||
|
||
void ScintillaEditView::convertSelectedTextTo(const TextCaseType & caseToConvert)
|
||
{
|
||
int selectionStart = execute(SCI_GETSELECTIONSTART);
|
||
int selectionEnd = execute(SCI_GETSELECTIONEND);
|
||
|
||
int strLen = (selectionEnd - selectionStart);
|
||
if (strLen != 0)
|
||
{
|
||
int strSize = strLen + 1;
|
||
|
||
char *selectedStr = new char[strSize];
|
||
|
||
execute(SCI_GETSELTEXT, 0, reinterpret_cast<sptr_t>(selectedStr));
|
||
|
||
QString utf8Str(selectedStr);
|
||
changeCase(caseToConvert, utf8Str);
|
||
|
||
QByteArray bytes = utf8Str.toUtf8();
|
||
|
||
execute(SCI_SETTARGETRANGE, selectionStart, selectionEnd);
|
||
|
||
execute(SCI_BEGINUNDOACTION);
|
||
execute(SCI_REPLACETARGET, strLen, reinterpret_cast<sptr_t>(bytes.data()));
|
||
execute(SCI_ENDUNDOACTION);
|
||
execute(SCI_SETSEL, selectionStart, selectionEnd);
|
||
delete[] selectedStr;
|
||
}
|
||
}
|
||
|
||
//获取当前选择的行范围
|
||
std::pair<size_t, size_t> ScintillaEditView::getSelectionLinesRange(intptr_t selectionNumber /* = -1 */) const
|
||
{
|
||
size_t numSelections = execute(SCI_GETSELECTIONS);
|
||
|
||
size_t start_pos, end_pos;
|
||
|
||
if ((selectionNumber < 0) || (static_cast<size_t>(selectionNumber) >= numSelections))
|
||
{
|
||
start_pos = execute(SCI_GETSELECTIONSTART);
|
||
end_pos = execute(SCI_GETSELECTIONEND);
|
||
}
|
||
else
|
||
{
|
||
start_pos = execute(SCI_GETSELECTIONNSTART, selectionNumber);
|
||
end_pos = execute(SCI_GETSELECTIONNEND, selectionNumber);
|
||
}
|
||
|
||
size_t line1 = execute(SCI_LINEFROMPOSITION, start_pos);
|
||
size_t line2 = execute(SCI_LINEFROMPOSITION, end_pos);
|
||
|
||
if ((line1 != line2) && (static_cast<size_t>(execute(SCI_POSITIONFROMLINE, line2)) == end_pos))
|
||
{
|
||
// if the end of the selection includes the line-ending,
|
||
// then don't include the following line in the range
|
||
--line2;
|
||
}
|
||
|
||
return std::pair<size_t, size_t>(line1, line2);
|
||
}
|
||
|
||
//获取内容。是uft8格式的。所有字符都以utf8编码
|
||
void ScintillaEditView::getText(char *dest, size_t start, size_t end) const
|
||
{
|
||
Sci_TextRange tr;
|
||
tr.chrg.cpMin = static_cast<Sci_PositionCR>(start);
|
||
tr.chrg.cpMax = static_cast<Sci_PositionCR>(end);
|
||
tr.lpstrText = dest;
|
||
|
||
this->SendScintilla(SCI_GETTEXTRANGE, 0, &tr);
|
||
}
|
||
|
||
QString ScintillaEditView::getGenericTextAsQString(size_t start, size_t end) const
|
||
{
|
||
assert(end > start);
|
||
const size_t bufSize = end - start + 1;
|
||
|
||
QByteArray bytes;
|
||
bytes.resize(bufSize);
|
||
getText(bytes.data(), start, end);
|
||
|
||
return QString(bytes);
|
||
}
|
||
|
||
QString ScintillaEditView::getEOLString()
|
||
{
|
||
intptr_t eol_mode = execute(SCI_GETEOLMODE);
|
||
if (eol_mode == SC_EOL_CRLF)
|
||
{
|
||
return QString("\r\n");
|
||
}
|
||
else if (eol_mode == SC_EOL_LF)
|
||
{
|
||
return QString("\n");
|
||
}
|
||
else
|
||
{
|
||
return QString("\r");
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
size_t vecRemoveDuplicates(QList<QString>& vec)
|
||
{
|
||
|
||
std::unordered_set<std::string> seen;
|
||
auto newEnd = std::remove_if(vec.begin(), vec.end(), [&seen](const QString& value)
|
||
{
|
||
return !seen.insert(value.toStdString()).second;
|
||
});
|
||
|
||
vec.erase(newEnd, vec.end());
|
||
|
||
return vec.size();
|
||
}
|
||
|
||
|
||
void ScintillaEditView::removeAnyDuplicateLines()
|
||
{
|
||
size_t fromLine = 0, toLine = 0;
|
||
bool hasLineSelection = false;
|
||
|
||
auto selStart = execute(SCI_GETSELECTIONSTART);
|
||
auto selEnd = execute(SCI_GETSELECTIONEND);
|
||
hasLineSelection = selStart != selEnd;
|
||
|
||
if (hasLineSelection)
|
||
{
|
||
const std::pair<size_t, size_t> lineRange = getSelectionLinesRange();
|
||
// One single line selection is not allowed.
|
||
if (lineRange.first == lineRange.second)
|
||
{
|
||
return;
|
||
}
|
||
fromLine = lineRange.first;
|
||
toLine = lineRange.second;
|
||
}
|
||
else
|
||
{
|
||
// No selection.
|
||
fromLine = 0;
|
||
toLine = execute(SCI_GETLINECOUNT) - 1;
|
||
}
|
||
|
||
if (fromLine >= toLine)
|
||
{
|
||
return;
|
||
}
|
||
|
||
const auto startPos = execute(SCI_POSITIONFROMLINE, fromLine);
|
||
const auto endPos = execute(SCI_POSITIONFROMLINE, toLine) + execute(SCI_LINELENGTH, toLine);
|
||
const QString text = getGenericTextAsQString(startPos, endPos);
|
||
|
||
QStringList linesVect = text.split(getEOLString());
|
||
|
||
const size_t lineCount = execute(SCI_GETLINECOUNT);
|
||
|
||
const bool doingEntireDocument = (toLine == (lineCount - 1));
|
||
if (!doingEntireDocument)
|
||
{
|
||
if (linesVect.rbegin()->isEmpty())
|
||
{
|
||
linesVect.pop_back();
|
||
}
|
||
}
|
||
|
||
size_t origSize = linesVect.size();
|
||
|
||
|
||
size_t newSize = vecRemoveDuplicates(linesVect);
|
||
|
||
if (origSize != newSize)
|
||
{
|
||
QString joined = linesVect.join(getEOLString());
|
||
|
||
if (!doingEntireDocument)
|
||
{
|
||
joined += getEOLString();
|
||
}
|
||
if (text != joined)
|
||
{
|
||
QByteArray str2replace;
|
||
str2replace = joined.toUtf8();
|
||
|
||
replaceTarget(str2replace, startPos, endPos);
|
||
}
|
||
}
|
||
}
|
||
|
||
void ScintillaEditView::insertCharsFrom(size_t position, const QByteArray & text2insert) const
|
||
{
|
||
this->SendScintilla(SCI_INSERTTEXT, position, text2insert.data());
|
||
}
|
||
|
||
void ScintillaEditView::appandGenericText(const QByteArray &text2Append) const
|
||
{
|
||
this->SendScintilla(SCI_APPENDTEXT, text2Append.size(), text2Append.data());
|
||
}
|
||
|
||
void ScintillaEditView::insertNewLineAboveCurrentLine(bool)
|
||
{
|
||
QString newline = getEOLString();
|
||
const auto current_line = getCurrentLineNumber();
|
||
if (current_line == 0)
|
||
{
|
||
// Special handling if caret is at first line.
|
||
insertCharsFrom(0, newline.toUtf8());
|
||
}
|
||
else
|
||
{
|
||
const auto eol_length = newline.length();
|
||
const auto position = execute(SCI_POSITIONFROMLINE, current_line) - eol_length;
|
||
insertCharsFrom(position, newline.toUtf8());
|
||
}
|
||
execute(SCI_SETEMPTYSELECTION, execute(SCI_POSITIONFROMLINE, current_line));
|
||
}
|
||
|
||
void ScintillaEditView::insertNewLineBelowCurrentLine(bool)
|
||
{
|
||
QString newline = getEOLString();
|
||
const auto line_count = execute(SCI_GETLINECOUNT);
|
||
const auto current_line = getCurrentLineNumber();
|
||
if (current_line == line_count - 1)
|
||
{
|
||
// Special handling if caret is at last line.
|
||
appandGenericText(newline.toUtf8());
|
||
}
|
||
else
|
||
{
|
||
const auto eol_length = newline.length();
|
||
const auto position = eol_length + execute(SCI_GETLINEENDPOSITION, current_line);
|
||
insertCharsFrom(position, newline.toUtf8());
|
||
}
|
||
execute(SCI_SETEMPTYSELECTION, execute(SCI_POSITIONFROMLINE, current_line + 1));
|
||
}
|
||
|
||
void ScintillaEditView::sortLines(size_t fromLine, size_t toLine, ISorter* pSort)
|
||
{
|
||
if (fromLine >= toLine)
|
||
{
|
||
return;
|
||
}
|
||
|
||
const auto startPos = execute(SCI_POSITIONFROMLINE, fromLine);
|
||
const auto endPos = execute(SCI_POSITIONFROMLINE, toLine) + execute(SCI_LINELENGTH, toLine);
|
||
|
||
const QString text = getGenericTextAsQString(startPos, endPos);
|
||
QStringList splitText = text.split(getEOLString());
|
||
|
||
|
||
const size_t lineCount = execute(SCI_GETLINECOUNT);
|
||
const bool sortEntireDocument = toLine == lineCount - 1;
|
||
if (!sortEntireDocument)
|
||
{
|
||
if (splitText.rbegin()->isEmpty())
|
||
{
|
||
splitText.pop_back();
|
||
}
|
||
}
|
||
assert(toLine - fromLine + 1 == splitText.size());
|
||
const QList<QString> sortedText = pSort->sort(splitText);
|
||
|
||
QString joined = sortedText.join(getEOLString());
|
||
if (sortEntireDocument)
|
||
{
|
||
assert(joined.length() == text.length());
|
||
}
|
||
else
|
||
{
|
||
assert(joined.length() + getEOLString().length() == text.length());
|
||
joined += getEOLString();
|
||
}
|
||
if (text != joined)
|
||
{
|
||
QByteArray bytes = joined.toUtf8();
|
||
replaceTarget(bytes, startPos, endPos);
|
||
}
|
||
}
|
||
|
||
void ScintillaEditView::setFoldColor(int margin, QColor fgClack, QColor bkColor, QColor foreActive)
|
||
{
|
||
SendScintilla(SCI_MARKERSETFORE, margin, fgClack);
|
||
SendScintilla(SCI_MARKERSETBACK, margin, bkColor);
|
||
SendScintilla(SCI_MARKERSETBACKSELECTED, margin, foreActive);
|
||
}
|
||
|
||
ColumnModeInfos ScintillaEditView::getColumnModeSelectInfo()
|
||
{
|
||
ColumnModeInfos columnModeInfos;
|
||
if (execute(SCI_GETSELECTIONS) > 1) // Multi-Selection || Column mode
|
||
{
|
||
intptr_t nbSel = execute(SCI_GETSELECTIONS);
|
||
|
||
for (int i = 0; i < nbSel; ++i)
|
||
{
|
||
intptr_t absPosSelStartPerLine = execute(SCI_GETSELECTIONNANCHOR, i);
|
||
intptr_t absPosSelEndPerLine = execute(SCI_GETSELECTIONNCARET, i);
|
||
intptr_t nbVirtualAnchorSpc = execute(SCI_GETSELECTIONNANCHORVIRTUALSPACE, i);
|
||
intptr_t nbVirtualCaretSpc = execute(SCI_GETSELECTIONNCARETVIRTUALSPACE, i);
|
||
|
||
if (absPosSelStartPerLine == absPosSelEndPerLine && execute(SCI_SELECTIONISRECTANGLE))
|
||
{
|
||
bool dir = nbVirtualAnchorSpc < nbVirtualCaretSpc ? L2R : R2L;
|
||
columnModeInfos.push_back(ColumnModeInfo(absPosSelStartPerLine, absPosSelEndPerLine, i, dir, nbVirtualAnchorSpc, nbVirtualCaretSpc));
|
||
}
|
||
else if (absPosSelStartPerLine > absPosSelEndPerLine)
|
||
columnModeInfos.push_back(ColumnModeInfo(absPosSelEndPerLine, absPosSelStartPerLine, i, R2L, nbVirtualAnchorSpc, nbVirtualCaretSpc));
|
||
else
|
||
columnModeInfos.push_back(ColumnModeInfo(absPosSelStartPerLine, absPosSelEndPerLine, i, L2R, nbVirtualAnchorSpc, nbVirtualCaretSpc));
|
||
}
|
||
}
|
||
return columnModeInfos;
|
||
}
|
||
|
||
|
||
void ScintillaEditView::columnReplace(ColumnModeInfos& cmi, QByteArray& str)
|
||
{
|
||
intptr_t totalDiff = 0;
|
||
for (size_t i = 0, len = cmi.size(); i < len; ++i)
|
||
{
|
||
if (cmi[i].isValid())
|
||
{
|
||
intptr_t len2beReplace = cmi[i]._selRpos - cmi[i]._selLpos;
|
||
intptr_t diff = str.size() - len2beReplace;
|
||
|
||
cmi[i]._selLpos += totalDiff;
|
||
cmi[i]._selRpos += totalDiff;
|
||
bool hasVirtualSpc = cmi[i]._nbVirtualAnchorSpc > 0;
|
||
|
||
if (hasVirtualSpc) // if virtual space is present, then insert space
|
||
{
|
||
for (intptr_t j = 0, k = cmi[i]._selLpos; j < cmi[i]._nbVirtualCaretSpc; ++j, ++k)
|
||
{
|
||
execute(SCI_INSERTTEXT, k, reinterpret_cast<sptr_t>(" "));
|
||
}
|
||
cmi[i]._selLpos += cmi[i]._nbVirtualAnchorSpc;
|
||
cmi[i]._selRpos += cmi[i]._nbVirtualCaretSpc;
|
||
}
|
||
|
||
execute(SCI_SETTARGETRANGE, cmi[i]._selLpos, cmi[i]._selRpos);
|
||
|
||
execute(SCI_REPLACETARGET, static_cast<int>(-1), reinterpret_cast<sptr_t>(str.data()));
|
||
|
||
if (hasVirtualSpc)
|
||
{
|
||
totalDiff += cmi[i]._nbVirtualAnchorSpc + str.size();
|
||
|
||
// Now there's no more virtual space
|
||
cmi[i]._nbVirtualAnchorSpc = 0;
|
||
cmi[i]._nbVirtualCaretSpc = 0;
|
||
}
|
||
else
|
||
{
|
||
totalDiff += diff;
|
||
}
|
||
cmi[i]._selRpos += diff;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
void ScintillaEditView::setMultiSelections(const ColumnModeInfos& cmi)
|
||
{
|
||
for (size_t i = 0, len = cmi.size(); i < len; ++i)
|
||
{
|
||
if (cmi[i].isValid())
|
||
{
|
||
intptr_t selStart = cmi[i]._direction == L2R ? cmi[i]._selLpos : cmi[i]._selRpos;
|
||
intptr_t selEnd = cmi[i]._direction == L2R ? cmi[i]._selRpos : cmi[i]._selLpos;
|
||
execute(SCI_SETSELECTIONNSTART, i, selStart);
|
||
execute(SCI_SETSELECTIONNEND, i, selEnd);
|
||
}
|
||
|
||
if (cmi[i]._nbVirtualAnchorSpc)
|
||
execute(SCI_SETSELECTIONNANCHORVIRTUALSPACE, i, cmi[i]._nbVirtualAnchorSpc);
|
||
if (cmi[i]._nbVirtualCaretSpc)
|
||
execute(SCI_SETSELECTIONNCARETVIRTUALSPACE, i, cmi[i]._nbVirtualCaretSpc);
|
||
}
|
||
}
|
||
|
||
int getNbDigits(int aNum, int base)
|
||
{
|
||
int nbChiffre = 1;
|
||
int diviseur = base;
|
||
|
||
for (;;)
|
||
{
|
||
int result = aNum / diviseur;
|
||
if (!result)
|
||
break;
|
||
else
|
||
{
|
||
diviseur *= base;
|
||
++nbChiffre;
|
||
}
|
||
}
|
||
if ((base == 16) && (nbChiffre % 2 != 0))
|
||
nbChiffre += 1;
|
||
|
||
return nbChiffre;
|
||
}
|
||
|
||
void ScintillaEditView::columnReplace(ColumnModeInfos& cmi, int initial, int incr, int repeat, int format, bool isCapital, QByteArray& prefix)
|
||
{
|
||
assert(repeat > 0);
|
||
|
||
// If there is no column mode info available, no need to do anything
|
||
// If required a message can be shown to user, that select column properly or something similar
|
||
// It is just a double check as taken in callee method (in case this method is called from multiple places)
|
||
if (cmi.size() <= 0)
|
||
return;
|
||
// 0000 00 00 : Dec BASE_10
|
||
// 0000 00 01 : Hex BASE_16
|
||
// 0000 00 10 : Oct BASE_08
|
||
// 0000 00 11 : Bin BASE_02
|
||
|
||
// 0000 01 00 : 0 leading
|
||
|
||
//Defined in ScintillaEditView.h :
|
||
//const UCHAR MASK_FORMAT = 0x03;
|
||
//const UCHAR MASK_ZERO_LEADING = 0x04;
|
||
|
||
int base = format;
|
||
|
||
const int stringSize = 512;
|
||
QByteArray str;
|
||
str.reserve(stringSize) ;
|
||
|
||
// Compute the numbers to be placed at each column.
|
||
std::vector<int> numbers;
|
||
{
|
||
int curNumber = initial;
|
||
const size_t kiMaxSize = cmi.size();
|
||
while (numbers.size() < kiMaxSize)
|
||
{
|
||
for (int i = 0; i < repeat; i++)
|
||
{
|
||
numbers.push_back(curNumber);
|
||
if (numbers.size() >= kiMaxSize)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
curNumber += incr;
|
||
}
|
||
}
|
||
|
||
assert(numbers.size() > 0);
|
||
|
||
/*const int kibEnd = getNbDigits(*numbers.rbegin(), base);
|
||
const int kibInit = getNbDigits(initial, base);
|
||
const int kib = std::max<int>(kibInit, kibEnd);*/
|
||
|
||
intptr_t totalDiff = 0;
|
||
const size_t len = cmi.size();
|
||
for (size_t i = 0; i < len; i++)
|
||
{
|
||
if (cmi[i].isValid())
|
||
{
|
||
const intptr_t len2beReplaced = cmi[i]._selRpos - cmi[i]._selLpos;
|
||
|
||
if (base != 16)
|
||
{
|
||
str = prefix + QString::number(numbers.at(i), base).toUtf8();
|
||
}
|
||
else
|
||
{
|
||
//16进制,判断大小写
|
||
if (isCapital)
|
||
{
|
||
str = prefix + QString::number(numbers.at(i), base).toUpper().toUtf8();
|
||
}
|
||
else
|
||
{
|
||
str = prefix + QString::number(numbers.at(i), base).toUtf8();
|
||
}
|
||
}
|
||
|
||
const intptr_t diff = str.size() - len2beReplaced;
|
||
|
||
cmi[i]._selLpos += totalDiff;
|
||
cmi[i]._selRpos += totalDiff;
|
||
|
||
|
||
|
||
const bool hasVirtualSpc = cmi[i]._nbVirtualAnchorSpc > 0;
|
||
if (hasVirtualSpc) // if virtual space is present, then insert space
|
||
{
|
||
for (intptr_t j = 0, k = cmi[i]._selLpos; j < cmi[i]._nbVirtualCaretSpc; ++j, ++k)
|
||
{
|
||
execute(SCI_INSERTTEXT, k, reinterpret_cast<sptr_t>(" "));
|
||
}
|
||
cmi[i]._selLpos += cmi[i]._nbVirtualAnchorSpc;
|
||
cmi[i]._selRpos += cmi[i]._nbVirtualCaretSpc;
|
||
}
|
||
execute(SCI_SETTARGETRANGE, cmi[i]._selLpos, cmi[i]._selRpos);
|
||
|
||
execute(SCI_REPLACETARGET, static_cast<int>(-1), reinterpret_cast<sptr_t>(str.data()));
|
||
|
||
if (hasVirtualSpc)
|
||
{
|
||
totalDiff += cmi[i]._nbVirtualAnchorSpc + str.size();
|
||
// Now there's no more virtual space
|
||
cmi[i]._nbVirtualAnchorSpc = 0;
|
||
cmi[i]._nbVirtualCaretSpc = 0;
|
||
}
|
||
else
|
||
{
|
||
totalDiff += diff;
|
||
}
|
||
cmi[i]._selRpos += diff;
|
||
}
|
||
}
|
||
}
|
||
|
||
void ScintillaEditView::getVisibleStartAndEndPosition(int * startPos, int * endPos)
|
||
{
|
||
assert(startPos != NULL && endPos != NULL);
|
||
// Get the position of the 1st and last showing chars from the edit view
|
||
QRect rcEditView;
|
||
rcEditView = this->rect();
|
||
int pos = execute(SCI_POSITIONFROMPOINT, 0, 0);
|
||
int line = execute(SCI_LINEFROMPOSITION, pos);
|
||
*startPos = static_cast<int32_t>(execute(SCI_POSITIONFROMLINE, line));
|
||
pos = execute(SCI_POSITIONFROMPOINT, rcEditView.right() - rcEditView.left(), rcEditView.bottom() - rcEditView.top());
|
||
line = execute(SCI_LINEFROMPOSITION, pos);
|
||
*endPos = static_cast<int32_t>(execute(SCI_GETLINEENDPOSITION, line));
|
||
}
|
||
|
||
bool isUrlSchemeStartChar(QChar const c)
|
||
{
|
||
return ((c >= 'A') && (c <= 'Z'))
|
||
|| ((c >= 'a') && (c <= 'z'));
|
||
}
|
||
|
||
bool isUrlSchemeDelimiter(QChar const c)
|
||
{
|
||
return !(((c >= '0') && (c <= '9'))
|
||
|| ((c >= 'A') && (c <= 'Z'))
|
||
|| ((c >= 'a') && (c <= 'z'))
|
||
|| (c == '_'));
|
||
}
|
||
|
||
bool scanToUrlStart(QString &text, int textLen, int start, int* distance, int* schemeLength)
|
||
{
|
||
int p = start;
|
||
int p0 = 0;
|
||
enum { sUnknown, sScheme } s = sUnknown;
|
||
while (p < textLen)
|
||
{
|
||
switch (s)
|
||
{
|
||
case sUnknown:
|
||
if (isUrlSchemeStartChar(text[p]) && ((p == 0) || isUrlSchemeDelimiter(text[p - 1])))
|
||
{
|
||
p0 = p;
|
||
s = sScheme;
|
||
}
|
||
break;
|
||
|
||
case sScheme:
|
||
if (text[p] == ':')
|
||
{
|
||
*distance = p0 - start;
|
||
*schemeLength = p - p0 + 1;
|
||
return true;
|
||
}
|
||
if (!isUrlSchemeStartChar(text[p]))
|
||
s = sUnknown;
|
||
break;
|
||
}
|
||
p++;
|
||
}
|
||
*schemeLength = 0;
|
||
*distance = p - start;
|
||
return false;
|
||
}
|
||
|
||
bool isUrlTextChar(QChar const c)
|
||
{
|
||
if (c <= ' ') return false;
|
||
switch (c.digitValue())
|
||
{
|
||
case ('"'):
|
||
case ('#'):
|
||
case ('<'):
|
||
case ('>'):
|
||
case ('{'):
|
||
case ('}'):
|
||
case ('?'):
|
||
case ('\u007F'):
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
bool isUrlQueryDelimiter(QChar const c)
|
||
{
|
||
switch (c.digitValue())
|
||
{
|
||
case '&':
|
||
case '+':
|
||
case '=':
|
||
case ';':
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
void scanToUrlEnd(QString & text, int textLen, int start, int* distance)
|
||
{
|
||
int p = start;
|
||
QChar q = 0;
|
||
enum { sHostAndPath, sQuery, sQueryAfterDelimiter, sQueryQuotes, sQueryAfterQuotes, sFragment } s = sHostAndPath;
|
||
while (p < textLen)
|
||
{
|
||
switch (s)
|
||
{
|
||
case sHostAndPath:
|
||
if (text[p] == QChar('?'))
|
||
s = sQuery;
|
||
else if (text[p] == '#')
|
||
s = sFragment;
|
||
else if (!isUrlTextChar(text[p]))
|
||
{
|
||
*distance = p - start;
|
||
return;
|
||
}
|
||
break;
|
||
|
||
case sQuery:
|
||
if (text[p] == '#')
|
||
s = sFragment;
|
||
else if (isUrlQueryDelimiter(text[p]))
|
||
s = sQueryAfterDelimiter;
|
||
else if (!isUrlTextChar(text[p]))
|
||
{
|
||
*distance = p - start;
|
||
return;
|
||
}
|
||
break;
|
||
|
||
case sQueryAfterDelimiter:
|
||
if ((text[p] == '\'') || (text[p] == '"') || (text[p] == '`'))
|
||
{
|
||
q = text[p];
|
||
s = sQueryQuotes;
|
||
}
|
||
else if (text[p] == '(')
|
||
{
|
||
q = ')';
|
||
s = sQueryQuotes;
|
||
}
|
||
else if (text[p] == '[')
|
||
{
|
||
q = ']';
|
||
s = sQueryQuotes;
|
||
}
|
||
else if (text[p] == '{')
|
||
{
|
||
q = '}';
|
||
s = sQueryQuotes;
|
||
}
|
||
else if (isUrlTextChar(text[p]))
|
||
s = sQuery;
|
||
else
|
||
{
|
||
*distance = p - start;
|
||
return;
|
||
}
|
||
break;
|
||
|
||
case sQueryQuotes:
|
||
if (text[p] < ' ')
|
||
{
|
||
*distance = p - start;
|
||
return;
|
||
}
|
||
if (text[p] == q)
|
||
s = sQueryAfterQuotes;
|
||
break;
|
||
|
||
case sQueryAfterQuotes:
|
||
if (isUrlQueryDelimiter(text[p]))
|
||
s = sQueryAfterDelimiter;
|
||
else
|
||
{
|
||
*distance = p - start;
|
||
return;
|
||
}
|
||
break;
|
||
|
||
case sFragment:
|
||
if (!isUrlTextChar(text[p]))
|
||
{
|
||
*distance = p - start;
|
||
return;
|
||
}
|
||
break;
|
||
}
|
||
p++;
|
||
}
|
||
*distance = p - start;
|
||
}
|
||
|
||
// removeUnwantedTrailingCharFromUrl removes a single unwanted trailing character from an URL.
|
||
// It has to be called repeatedly, until it returns false, meaning that all unwanted characters are gone.
|
||
bool removeUnwantedTrailingCharFromUrl(QChar const *text, int* length)
|
||
{
|
||
int l = *length - 1;
|
||
if (l <= 0) return false;
|
||
{ // remove unwanted single characters
|
||
const char *singleChars = ".,:;?!#";
|
||
for (int i = 0; singleChars[i]; i++)
|
||
if (text[l] == singleChars[i])
|
||
{
|
||
*length = l;
|
||
return true;
|
||
}
|
||
}
|
||
{ // remove unwanted closing parenthesis
|
||
const char *closingParenthesis = ")]";
|
||
const char *openingParenthesis = "([";
|
||
for (int i = 0; closingParenthesis[i]; i++)
|
||
if (text[l] == closingParenthesis[i])
|
||
{
|
||
int count = 0;
|
||
for (int j = l - 1; j >= 0; j--)
|
||
{
|
||
if (text[j] == closingParenthesis[i])
|
||
count++;
|
||
if (text[j] == openingParenthesis[i])
|
||
if (count > 0)
|
||
count--;
|
||
else
|
||
return false;
|
||
}
|
||
if (count != 0)
|
||
return false;
|
||
*length = l;
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
bool isUrl(QString& text, int textLen, int start, int* segmentLen)
|
||
{
|
||
int dist = 0, schemeLen = 0;
|
||
if (scanToUrlStart(text, textLen, start, &dist, &schemeLen))
|
||
{
|
||
if (dist)
|
||
{
|
||
*segmentLen = dist;
|
||
return false;
|
||
}
|
||
int len = 0;
|
||
scanToUrlEnd(text, textLen, start + schemeLen, &len);
|
||
if (len)
|
||
{
|
||
len += schemeLen;
|
||
|
||
QString urlStr = text.mid(start, len);
|
||
if (urlStr.startsWith("http://") || urlStr.startsWith("https://"))
|
||
{
|
||
|
||
QUrl url(urlStr);
|
||
|
||
bool r = url.isValid();
|
||
if (r)
|
||
{
|
||
while (removeUnwantedTrailingCharFromUrl(text.data() + start, &len));
|
||
*segmentLen = len;
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
len = 1;
|
||
int lMax = textLen - start;
|
||
while (isUrlSchemeStartChar(text[start + len]) && (len < lMax)) len++;
|
||
*segmentLen = len;
|
||
return false;
|
||
}
|
||
*segmentLen = dist;
|
||
return false;
|
||
}
|
||
|
||
quint32 ScintillaEditView::getBigTextBlockStartLine()
|
||
{
|
||
return m_curBlockLineStartNum;
|
||
}
|
||
|
||
void ScintillaEditView::setBigTextBlockStartLine(quint32 line)
|
||
{
|
||
m_curBlockLineStartNum = line;
|
||
}
|
||
|
||
void ScintillaEditView::addHotSpot()
|
||
{
|
||
if (CCNotePad::s_hightWebAddr == 1)
|
||
{
|
||
int urlAction = urlNoUnderLineFg;
|
||
int indicStyle = INDIC_PLAIN;
|
||
int indicHoverStyle = INDIC_EXPLORERLINK;
|
||
int indicStyleCur = this->execute(SCI_INDICGETSTYLE, URL_INDIC);
|
||
int indicHoverStyleCur = this->execute(SCI_INDICGETHOVERSTYLE, URL_INDIC);
|
||
|
||
if ((indicStyleCur != indicStyle) || (indicHoverStyleCur != indicHoverStyle))
|
||
{
|
||
this->execute(SCI_INDICSETSTYLE, URL_INDIC, indicStyle);
|
||
this->execute(SCI_INDICSETHOVERSTYLE, URL_INDIC, indicHoverStyle);
|
||
this->execute(SCI_INDICSETALPHA, URL_INDIC, 70);
|
||
this->execute(SCI_INDICSETFLAGS, URL_INDIC, SC_INDICFLAG_VALUEFORE);
|
||
}
|
||
|
||
int startPos = 0;
|
||
int endPos = -1;
|
||
this->getVisibleStartAndEndPosition(&startPos, &endPos);
|
||
if (startPos >= endPos)
|
||
{
|
||
return;
|
||
}
|
||
|
||
this->execute(SCI_SETINDICATORCURRENT, URL_INDIC);
|
||
if (urlAction == urlDisable)
|
||
{
|
||
this->execute(SCI_INDICATORCLEARRANGE, startPos, endPos - startPos);
|
||
return;
|
||
}
|
||
|
||
int indicFore = this->execute(SCI_STYLEGETFORE, STYLE_DEFAULT);
|
||
this->execute(SCI_SETINDICATORVALUE, indicFore);
|
||
|
||
QByteArray encodedText;
|
||
encodedText.resize(endPos - startPos);
|
||
this->getText(encodedText.data(), startPos, endPos);
|
||
|
||
QString encodedTextStr(encodedText);
|
||
|
||
int wideTextLen = encodedTextStr.size();
|
||
|
||
|
||
if (wideTextLen > 0)
|
||
{
|
||
int startWide = 0;
|
||
int lenWide = 0;
|
||
int startEncoded = 0;
|
||
int lenEncoded = 0;
|
||
while (true)
|
||
{
|
||
bool r = isUrl(encodedTextStr, encodedTextStr.size(), startWide, &lenWide);
|
||
if (lenWide <= 0)
|
||
break;
|
||
|
||
lenEncoded = encodedTextStr.mid(startWide, lenWide).toUtf8().size();
|
||
|
||
if (r)
|
||
this->execute(SCI_INDICATORFILLRANGE, startEncoded + startPos, lenEncoded);
|
||
else
|
||
this->execute(SCI_INDICATORCLEARRANGE, startEncoded + startPos, lenEncoded);
|
||
|
||
startWide += lenWide;
|
||
startEncoded += lenEncoded;
|
||
|
||
if ((startWide >= wideTextLen) || ((startEncoded + startPos) >= endPos))
|
||
break;
|
||
}
|
||
assert((startEncoded + startPos) == endPos);
|
||
assert(startWide == wideTextLen);
|
||
}
|
||
}
|
||
}
|
||
|
||
void ScintillaEditView::setStyleOptions()
|
||
{
|
||
#if 0
|
||
//如果是黑色主题,则单独做一些风格设置
|
||
if (StyleSet::m_curStyleId == BLACK_SE)
|
||
{
|
||
setCaretLineBackgroundColor(QColor(0x333333));
|
||
setMatchedBraceForegroundColor(QColor(246, 81, 246));
|
||
setMatchedBraceBackgroundColor(QColor(18, 90, 36));
|
||
setCaretForegroundColor(QColor(255, 255, 255));
|
||
setFoldColor(SC_MARKNUM_FOLDEROPEN, QColor(45, 130, 45), QColor(222, 222, 222));
|
||
setFoldColor(SC_MARKNUM_FOLDER, QColor(45, 130, 45), QColor(222, 222, 222));
|
||
setFoldColor(SC_MARKNUM_FOLDERSUB, QColor(45, 130, 45), QColor(222, 222, 222));
|
||
setFoldColor(SC_MARKNUM_FOLDERTAIL, QColor(45, 130, 45), QColor(222, 222, 222));
|
||
setFoldColor(SC_MARKNUM_FOLDEREND, QColor(45, 130, 45), QColor(222, 222, 222));
|
||
setFoldColor(SC_MARKNUM_FOLDEROPENMID, QColor(45, 130, 45), QColor(222, 222, 222));
|
||
setFoldColor(SC_MARKNUM_FOLDERMIDTAIL, QColor(45, 130, 45), QColor(222, 222, 222));
|
||
}
|
||
else
|
||
{
|
||
setCaretLineBackgroundColor(QColor(0xe8e8ff));
|
||
setMatchedBraceForegroundColor(QColor(191, 141, 255));
|
||
setMatchedBraceBackgroundColor(QColor(222, 222, 222));
|
||
setCaretForegroundColor(QColor(0, 0, 0));
|
||
setFoldColor(SC_MARKNUM_FOLDEROPEN, QColor(Qt::white), QColor(128, 128, 128));
|
||
setFoldColor(SC_MARKNUM_FOLDER, QColor(Qt::white), QColor(128, 128, 128));
|
||
setFoldColor(SC_MARKNUM_FOLDERSUB, QColor(Qt::white), QColor(128, 128, 128));
|
||
setFoldColor(SC_MARKNUM_FOLDERTAIL, QColor(Qt::white), QColor(128, 128, 128));
|
||
setFoldColor(SC_MARKNUM_FOLDEREND, QColor(Qt::white), QColor(128, 128, 128));
|
||
setFoldColor(SC_MARKNUM_FOLDEROPENMID, QColor(Qt::white), QColor(128, 128, 128));
|
||
setFoldColor(SC_MARKNUM_FOLDERMIDTAIL, QColor(Qt::white), QColor(128, 128, 128));
|
||
}
|
||
#endif
|
||
}
|
||
|
||
//在ScintillaEditView中直接设置这些One_Stype_Info的属性值
|
||
//这里的style不一定是在lexer中的,而是默认的STYLE_*自定义的那些全局内部风格。
|
||
void ScintillaEditView::updateThemes()
|
||
{
|
||
//第0个是GLOBAL_OVERRIDE,是Lexer的样式,不属于全局
|
||
for (int i = 1; i <= URL_HOVERRED; ++i)
|
||
{
|
||
setGlobalFgColor(i);
|
||
setGlobalBgColor(i);
|
||
setGlobalFont(i);
|
||
}
|
||
}
|
||
|
||
void getFoldColor(QColor& fgColor, QColor& bgColor, QColor& activeFgColor)
|
||
{
|
||
//这里看起来反了,但是实际代码就是如此
|
||
fgColor = StyleSet::s_global_style->fold.bgColor;
|
||
bgColor = StyleSet::s_global_style->fold.fgColor;
|
||
|
||
activeFgColor = StyleSet::s_global_style->fold_active.fgColor;
|
||
|
||
}
|
||
|
||
void ScintillaEditView::setGlobalFgColor(int style)
|
||
{
|
||
switch (style)
|
||
{
|
||
case GLOBAL_OVERRIDE:
|
||
{
|
||
//全局前景色修改,是针对语法的lexer属性进行的修改,而非其余全局属性的修改。这点要区分开来
|
||
}
|
||
break;
|
||
|
||
case DEFAULT_STYLE:
|
||
{
|
||
//修改默认前景色
|
||
SendScintilla(SCI_STYLESETFORE, StyleSet::s_global_style->default_style.styleId, StyleSet::s_global_style->default_style.fgColor);
|
||
}
|
||
break;
|
||
|
||
case INDENT_GUIDELINE:
|
||
{
|
||
//修改默认前景色
|
||
SendScintilla(SCI_STYLESETFORE, StyleSet::s_global_style->indent_guideline.styleId, StyleSet::s_global_style->indent_guideline.fgColor);
|
||
}
|
||
break;
|
||
|
||
case BRACE_HIGHIGHT:
|
||
{
|
||
//修改默认前景色
|
||
SendScintilla(SCI_STYLESETFORE, StyleSet::s_global_style->brace_highight.styleId, StyleSet::s_global_style->brace_highight.fgColor);
|
||
}
|
||
break;
|
||
|
||
case BAD_BRACE_COLOUR:
|
||
{
|
||
//修改默认前景色
|
||
SendScintilla(SCI_STYLESETFORE, StyleSet::s_global_style->bad_brace_color.styleId, StyleSet::s_global_style->bad_brace_color.fgColor);
|
||
}
|
||
break;
|
||
|
||
case CURRENT_LINE_BACKGROUND_COLOR:
|
||
//不能设置前景色,只能设置背景
|
||
break;
|
||
|
||
case SELECT_TEXT_COLOR:
|
||
SendScintilla(SCI_SETSELFORE, true, StyleSet::s_global_style->select_text_color.fgColor);
|
||
break;
|
||
|
||
case CARET_COLOUR:
|
||
SendScintilla(SCI_SETCARETFORE, StyleSet::s_global_style->caret_colour.fgColor);
|
||
break;
|
||
|
||
case EDGE_COLOUR:
|
||
SendScintilla(SCI_SETEDGECOLOUR, StyleSet::s_global_style->edge_colour.fgColor);
|
||
break;
|
||
|
||
case LINE_NUMBER_MARGIN:
|
||
{
|
||
//修改默认前景色
|
||
SendScintilla(SCI_STYLESETFORE, StyleSet::s_global_style->line_number_margin.styleId, StyleSet::s_global_style->line_number_margin.fgColor);
|
||
}
|
||
break;
|
||
|
||
case BOOKMARK_MARGIN:
|
||
//不能设置
|
||
break;
|
||
|
||
case FOLD:
|
||
case FOLD_ACTIVE:
|
||
{
|
||
QColor foldfgColor = Qt::white, foldbgColor = Qt::gray, activeFoldFgColor = Qt::red;
|
||
getFoldColor(foldfgColor, foldbgColor, activeFoldFgColor);
|
||
|
||
setFoldColor(SC_MARKNUM_FOLDEROPEN, foldfgColor, foldbgColor, activeFoldFgColor);
|
||
setFoldColor(SC_MARKNUM_FOLDER, foldfgColor, foldbgColor, activeFoldFgColor);
|
||
setFoldColor(SC_MARKNUM_FOLDERSUB, foldfgColor, foldbgColor, activeFoldFgColor);
|
||
setFoldColor(SC_MARKNUM_FOLDERTAIL, foldfgColor, foldbgColor, activeFoldFgColor);
|
||
setFoldColor(SC_MARKNUM_FOLDEREND, foldfgColor, foldbgColor, activeFoldFgColor);
|
||
setFoldColor(SC_MARKNUM_FOLDEROPENMID, foldfgColor, foldbgColor, activeFoldFgColor);
|
||
setFoldColor(SC_MARKNUM_FOLDERMIDTAIL, foldfgColor, foldbgColor, activeFoldFgColor);
|
||
|
||
//暂时开启,看看后面是否有错误
|
||
//execute(SCI_MARKERENABLEHIGHLIGHT, true);
|
||
}
|
||
break;
|
||
|
||
case FOLD_MARGIN:
|
||
//前景背景一起设置,不分开
|
||
{
|
||
setFoldMarginColors(StyleSet::s_global_style->fold_margin.fgColor, StyleSet::s_global_style->fold_margin.bgColor);
|
||
}
|
||
break;
|
||
|
||
case WHITE_SPACE_SYMBOL:
|
||
SendScintilla(SCI_SETWHITESPACEFORE, true, StyleSet::s_global_style->white_space_stybol.fgColor);
|
||
break;
|
||
|
||
case SMART_HIGHLIGHTING:
|
||
//只能设置前景,不能设置背景
|
||
SendScintilla(SCI_INDICSETFORE, SCE_UNIVERSAL_FOUND_STYLE_SMART, StyleSet::s_global_style->smart_highlighting.fgColor);
|
||
break;
|
||
|
||
case FIND_MARK_STYLE:
|
||
//只能设置前景,不能设置背景。目前这条是空的,暂时没有使用
|
||
SendScintilla(SCI_INDICSETFORE, SCE_UNIVERSAL_FOUND_STYLE, StyleSet::s_global_style->find_mark_style.fgColor);
|
||
break;
|
||
|
||
case MARK_STYLE_1:
|
||
case MARK_STYLE_2:
|
||
case MARK_STYLE_3:
|
||
case MARK_STYLE_4:
|
||
case MARK_STYLE_5:
|
||
case INCREMENTAL_HIGHLIGHT:
|
||
//暂时没有使用
|
||
break;
|
||
|
||
case TAGS_MATCH_HIGHLIGHT:
|
||
SendScintilla(SCI_INDICSETFORE, SCE_UNIVERSAL_TAGMATCH, StyleSet::s_global_style->tags_match_highlight.fgColor);
|
||
break;
|
||
|
||
case TAGS_ATTRIBUTE:
|
||
SendScintilla(SCI_INDICSETFORE, SCE_UNIVERSAL_TAGATTR, StyleSet::s_global_style->tags_attribute.fgColor);
|
||
break;
|
||
|
||
//case ACTIVE_TAB_FOCUSED:
|
||
//case ACTIVE_TAB_UNFOCUSED:
|
||
//case ACTIVE_TAB_TEXT:
|
||
//case INACTIVE_TABS:
|
||
// break;
|
||
|
||
case URL_HOVERRED:
|
||
SendScintilla(SCI_INDICSETHOVERFORE, URL_INDIC, StyleSet::s_global_style->url_hoverred.fgColor);
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
};
|
||
|
||
void ScintillaEditView::setGlobalBgColor(int style)
|
||
{
|
||
switch (style)
|
||
{
|
||
case GLOBAL_OVERRIDE:
|
||
{
|
||
|
||
}
|
||
break;
|
||
|
||
case DEFAULT_STYLE:
|
||
{
|
||
//修改默认前景色
|
||
SendScintilla(SCI_STYLESETBACK, StyleSet::s_global_style->default_style.styleId, StyleSet::s_global_style->default_style.bgColor);
|
||
}
|
||
break;
|
||
|
||
case INDENT_GUIDELINE:
|
||
{
|
||
//修改默认前景色
|
||
SendScintilla(SCI_STYLESETBACK, StyleSet::s_global_style->indent_guideline.styleId, StyleSet::s_global_style->indent_guideline.bgColor);
|
||
}
|
||
break;
|
||
|
||
case BRACE_HIGHIGHT:
|
||
{
|
||
//修改默认前景色
|
||
SendScintilla(SCI_STYLESETBACK, StyleSet::s_global_style->brace_highight.styleId, StyleSet::s_global_style->brace_highight.bgColor);
|
||
}
|
||
break;
|
||
|
||
case BAD_BRACE_COLOUR:
|
||
{
|
||
//修改默认前景色
|
||
SendScintilla(SCI_STYLESETBACK, StyleSet::s_global_style->bad_brace_color.styleId, StyleSet::s_global_style->bad_brace_color.bgColor);
|
||
}
|
||
break;
|
||
|
||
case CURRENT_LINE_BACKGROUND_COLOR:
|
||
//不能设置前景色,只能设置背景
|
||
SendScintilla(SCI_SETCARETLINEBACK, StyleSet::s_global_style->current_line_background_color.bgColor);
|
||
break;
|
||
|
||
case SELECT_TEXT_COLOR:
|
||
SendScintilla(SCI_SETSELBACK, true, StyleSet::s_global_style->select_text_color.bgColor);
|
||
break;
|
||
|
||
case CARET_COLOUR:
|
||
//不能设置
|
||
break;
|
||
|
||
case EDGE_COLOUR:
|
||
//不能设置
|
||
break;
|
||
|
||
case LINE_NUMBER_MARGIN:
|
||
{
|
||
//修改默认背景色
|
||
SendScintilla(SCI_STYLESETBACK, StyleSet::s_global_style->line_number_margin.styleId, StyleSet::s_global_style->line_number_margin.bgColor);
|
||
}
|
||
break;
|
||
|
||
case BOOKMARK_MARGIN:
|
||
{
|
||
if (StyleSet::s_global_style->bookmark_margin.bgColor.isValid())
|
||
{
|
||
SendScintilla(SCI_SETMARGINBACKN, _SC_MARGE_SYBOLE, StyleSet::s_global_style->bookmark_margin.bgColor);
|
||
}
|
||
else
|
||
{
|
||
SendScintilla(SCI_SETMARGINBACKN, _SC_MARGE_SYBOLE, StyleSet::s_global_style->line_number_margin.bgColor);
|
||
}
|
||
}
|
||
break;
|
||
|
||
case FOLD:
|
||
case FOLD_ACTIVE:
|
||
{
|
||
QColor foldfgColor = Qt::white, foldbgColor = Qt::gray, activeFoldFgColor = Qt::red;
|
||
getFoldColor(foldfgColor, foldbgColor, activeFoldFgColor);
|
||
|
||
setFoldColor(SC_MARKNUM_FOLDEROPEN, foldfgColor, foldbgColor, activeFoldFgColor);
|
||
setFoldColor(SC_MARKNUM_FOLDER, foldfgColor, foldbgColor, activeFoldFgColor);
|
||
setFoldColor(SC_MARKNUM_FOLDERSUB, foldfgColor, foldbgColor, activeFoldFgColor);
|
||
setFoldColor(SC_MARKNUM_FOLDERTAIL, foldfgColor, foldbgColor, activeFoldFgColor);
|
||
setFoldColor(SC_MARKNUM_FOLDEREND, foldfgColor, foldbgColor, activeFoldFgColor);
|
||
setFoldColor(SC_MARKNUM_FOLDEROPENMID, foldfgColor, foldbgColor, activeFoldFgColor);
|
||
setFoldColor(SC_MARKNUM_FOLDERMIDTAIL, foldfgColor, foldbgColor, activeFoldFgColor);
|
||
|
||
//暂时不能开启。因为QT下面有一个1Pix的差异,如果开启,当前fold的变化会缺失1pix的宽度,看起来难看。
|
||
// 这是QT的bug,暂时解决不了。
|
||
//execute(SCI_MARKERENABLEHIGHLIGHT, true);
|
||
}
|
||
break;
|
||
|
||
case FOLD_MARGIN:
|
||
//前景背景一起设置,不分开
|
||
{
|
||
setFoldMarginColors(StyleSet::s_global_style->fold_margin.fgColor, StyleSet::s_global_style->fold_margin.bgColor);
|
||
}
|
||
break;
|
||
|
||
case WHITE_SPACE_SYMBOL:
|
||
//不能设置
|
||
break;
|
||
|
||
case SMART_HIGHLIGHTING:
|
||
//不能设置
|
||
break;
|
||
|
||
case FIND_MARK_STYLE:
|
||
//不能设置
|
||
break;
|
||
|
||
//下面五个比较特殊,选择改动的是背景。单本质改动的样式却是前景
|
||
case MARK_STYLE_1:
|
||
//只能设置前景,不能设置背景
|
||
SendScintilla(SCI_INDICSETFORE, SCE_UNIVERSAL_FOUND_STYLE_EXT5, StyleSet::s_global_style->mark_style_1.bgColor);
|
||
changeStyleColor(0);
|
||
break;
|
||
|
||
case MARK_STYLE_2:
|
||
//只能设置前景,不能设置背景
|
||
SendScintilla(SCI_INDICSETFORE, SCE_UNIVERSAL_FOUND_STYLE_EXT4, StyleSet::s_global_style->mark_style_2.bgColor);
|
||
changeStyleColor(1);
|
||
break;
|
||
|
||
case MARK_STYLE_3:
|
||
//只能设置前景,不能设置背景
|
||
SendScintilla(SCI_INDICSETFORE, SCE_UNIVERSAL_FOUND_STYLE_EXT3, StyleSet::s_global_style->mark_style_3.bgColor);
|
||
changeStyleColor(2);
|
||
break;
|
||
|
||
case MARK_STYLE_4:
|
||
//只能设置前景,不能设置背景
|
||
SendScintilla(SCI_INDICSETFORE, SCE_UNIVERSAL_FOUND_STYLE_EXT2, StyleSet::s_global_style->mark_style_4.bgColor);
|
||
changeStyleColor(3);
|
||
break;
|
||
|
||
case MARK_STYLE_5:
|
||
//只能设置前景,不能设置背景
|
||
SendScintilla(SCI_INDICSETFORE, SCE_UNIVERSAL_FOUND_STYLE_EXT1, StyleSet::s_global_style->mark_style_5.bgColor);
|
||
changeStyleColor(4);
|
||
break;
|
||
|
||
case INCREMENTAL_HIGHLIGHT:
|
||
case TAGS_MATCH_HIGHLIGHT:
|
||
case TAGS_ATTRIBUTE:
|
||
break;
|
||
|
||
//case ACTIVE_TAB_FOCUSED:
|
||
//case ACTIVE_TAB_UNFOCUSED:
|
||
//case ACTIVE_TAB_TEXT:
|
||
//case INACTIVE_TABS:
|
||
// break;
|
||
case URL_HOVERRED:
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
};
|
||
|
||
// Set the font for a style.
|
||
//enum Font_Set_Bit {
|
||
// Bold_Bit = 0x1,
|
||
// Italic_Bit = 0x2,
|
||
// Underline_Bit = 0x4,
|
||
// Font_Name_Bit = 0x8,
|
||
// Font_Size_Bit = 0x10,
|
||
//};
|
||
void ScintillaEditView::setStylesFont(const QFont& f, int style, int setBitMask)
|
||
{
|
||
if (setBitMask & Font_Name_Bit)
|
||
{
|
||
SendScintilla(SCI_STYLESETFONT, style, f.family().toUtf8().data());
|
||
}
|
||
if (setBitMask & Bold_Bit)
|
||
{
|
||
SendScintilla(SCI_STYLESETBOLD, style, f.bold());
|
||
}
|
||
if (setBitMask & Italic_Bit)
|
||
{
|
||
SendScintilla(SCI_STYLESETITALIC, style, f.italic());
|
||
}
|
||
if (setBitMask & Underline_Bit)
|
||
{
|
||
SendScintilla(SCI_STYLESETUNDERLINE, style, f.underline());
|
||
}
|
||
|
||
if (setBitMask & Font_Size_Bit && f.pointSize() > 2)
|
||
{
|
||
SendScintilla(SCI_STYLESETSIZE, style, f.pointSize());
|
||
}
|
||
//如果是行号大小变化,则必须动态计算行宽度
|
||
if (style == STYLE_LINENUMBER)
|
||
{
|
||
updateLineNumberWidth(1);
|
||
}
|
||
}
|
||
|
||
void ScintillaEditView::setGlobalFont(int style)
|
||
{
|
||
switch (style)
|
||
{
|
||
case GLOBAL_OVERRIDE:
|
||
case INDENT_GUIDELINE:
|
||
case CURRENT_LINE_BACKGROUND_COLOR:
|
||
case SELECT_TEXT_COLOR:
|
||
case CARET_COLOUR:
|
||
case EDGE_COLOUR:
|
||
case BOOKMARK_MARGIN:
|
||
case FOLD:
|
||
case FOLD_ACTIVE:
|
||
case FOLD_MARGIN:
|
||
case WHITE_SPACE_SYMBOL:
|
||
case SMART_HIGHLIGHTING:
|
||
case FIND_MARK_STYLE:
|
||
case MARK_STYLE_1:
|
||
case MARK_STYLE_2:
|
||
case MARK_STYLE_3:
|
||
case MARK_STYLE_4:
|
||
case MARK_STYLE_5:
|
||
case INCREMENTAL_HIGHLIGHT:
|
||
case TAGS_MATCH_HIGHLIGHT:
|
||
case TAGS_ATTRIBUTE:
|
||
//case ACTIVE_TAB_FOCUSED:
|
||
//case ACTIVE_TAB_UNFOCUSED:
|
||
//case ACTIVE_TAB_TEXT:
|
||
//case INACTIVE_TABS:
|
||
case URL_HOVERRED:
|
||
break;
|
||
|
||
case DEFAULT_STYLE:
|
||
{
|
||
setStylesFont(StyleSet::s_global_style->default_style.font, STYLE_DEFAULT, 0x1f);
|
||
}
|
||
break;
|
||
|
||
case BRACE_HIGHIGHT:
|
||
{
|
||
setStylesFont(StyleSet::s_global_style->brace_highight.font, STYLE_BRACELIGHT, 0x1f);
|
||
}
|
||
break;
|
||
|
||
case BAD_BRACE_COLOUR:
|
||
{
|
||
setStylesFont(StyleSet::s_global_style->bad_brace_color.font, STYLE_BRACEBAD, 0x1f);
|
||
}
|
||
break;
|
||
|
||
case LINE_NUMBER_MARGIN:
|
||
{
|
||
//除了下划线不加,其余的都需要设置
|
||
setStylesFont(StyleSet::s_global_style->line_number_margin.font, STYLE_LINENUMBER, 0x1b);
|
||
}
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
}
|