qscidisplaywindow.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. #include "qscidisplaywindow.h"
  2. #include "MediatorDisplay.h"
  3. #include "textfind.h"
  4. #include "common.h"
  5. #include "styleset.h"
  6. #include "rcglobal.h"
  7. #include <QScrollBar>
  8. #include <QFileInfo>
  9. #include <QProcess>
  10. #include <QMessageBox>
  11. #include <stdexcept>
  12. #include <SciLexer.h>
  13. QsciDisplayWindow::QsciDisplayWindow(QWidget *parent):QsciScintilla(parent), m_textFindWin(nullptr), m_preFirstLineNum(0), m_isShowFindItem(true), m_hasHighlight(false)
  14. {
  15. //20210815 左右行同步还有问题,暂时不屏蔽,不实现
  16. connect(this->verticalScrollBar(), &QScrollBar::valueChanged, this, &QsciDisplayWindow::slot_scrollYValueChange);
  17. connect(this->horizontalScrollBar(), &QScrollBar::valueChanged, this, &QsciDisplayWindow::slot_scrollXValueChange);
  18. setAcceptDrops(false);
  19. m_findStartPos = 0;
  20. m_findEndPos = 0;
  21. m_findCurPos = 0;
  22. m_pScintillaFunc = (SCINTILLA_FUNC)this->SendScintillaPtrResult(SCI_GETDIRECTFUNCTION);
  23. m_pScintillaPtr = (SCINTILLA_PTR)this->SendScintillaPtrResult(SCI_GETDIRECTPOINTER);
  24. if (!m_pScintillaFunc)
  25. {
  26. throw std::runtime_error("ScintillaEditView::init : SCI_GETDIRECTFUNCTION message failed");
  27. }
  28. if (!m_pScintillaPtr)
  29. {
  30. throw std::runtime_error("ScintillaEditView::init : SCI_GETDIRECTPOINTER message failed");
  31. }
  32. //这个无比要设置false,否则双击后高亮单词,拷贝时会拷贝多个选择。
  33. execute(SCI_SETMULTIPLESELECTION, true);
  34. execute(SCI_SETMULTIPASTE, 1);
  35. execute(SCI_SETADDITIONALCARETSVISIBLE, false);
  36. execute(SCI_SETSELFORE, true, 0x0);
  37. execute(SCI_SETSELBACK, true, 0x00ffff);
  38. //QColor foldfgColor(StyleSet::foldfgColor);
  39. //QColor foldbgColor(StyleSet::foldbgColor);//默认0xff,0xff,0xff
  40. ////通过fold发现,尽量使用qscint的功能,因为他做了大量封装和简化
  41. //setFolding(BoxedTreeFoldStyle, 2);
  42. //setFoldMarginColors(foldfgColor, foldbgColor);
  43. //setMarginsBackgroundColor(StyleSet::marginsBackgroundColor); //0xea, 0xf7, 0xff //默认0xf0f0f0
  44. //双击后同样的字段进行高亮
  45. execute(SCI_INDICSETSTYLE, SCE_UNIVERSAL_FOUND_STYLE_SMART, INDIC_ROUNDBOX);
  46. execute(SCI_INDICSETALPHA, SCE_UNIVERSAL_FOUND_STYLE_SMART, 100);
  47. execute(SCI_INDICSETUNDER, SCE_UNIVERSAL_FOUND_STYLE_SMART, false);
  48. execute(SCI_INDICSETFORE, SCE_UNIVERSAL_FOUND_STYLE_SMART, 0x00ff00);
  49. setStyleOptions();
  50. //开启后可以保证长行在滚动条下完整显示
  51. execute(SCI_SETSCROLLWIDTHTRACKING, true);
  52. connect(this, &QsciScintilla::selectionChanged, this, &QsciDisplayWindow::slot_clearHightWord, Qt::QueuedConnection);
  53. connect(this, &QsciDisplayWindow::delayWork, this, &QsciDisplayWindow::slot_delayWork, Qt::QueuedConnection);
  54. }
  55. QsciDisplayWindow::~QsciDisplayWindow()
  56. {
  57. if (m_textFindWin != nullptr)
  58. {
  59. delete m_textFindWin;
  60. m_textFindWin = nullptr;
  61. }
  62. }
  63. void QsciDisplayWindow::setFoldColor(int margin, QColor fgClack, QColor bkColor)
  64. {
  65. SendScintilla(SCI_MARKERSETFORE, margin, fgClack);
  66. SendScintilla(SCI_MARKERSETBACK, margin, bkColor);
  67. }
  68. void QsciDisplayWindow::setStyleOptions()
  69. {
  70. if (StyleSet::m_curStyleId != BLACK_SE)
  71. {
  72. setMarginsForegroundColor(QColor(0x80, 0x80, 0x80)); //默认0x80, 0x80, 0x80
  73. }
  74. else
  75. setMarginsBackgroundColor(0xf0f0f0);
  76. setFoldMarginColors(0xf0f0f0, 0xf0f0f0);
  77. {
  78. //setCaretLineBackgroundColor(QColor(0xe8e8ff));
  79. setCaretLineBackgroundColor(QColor(0xFAF9DE));
  80. setMatchedBraceForegroundColor(QColor(191, 141, 255));
  81. setMatchedBraceBackgroundColor(QColor(222, 222, 222));
  82. setCaretForegroundColor(QColor(0, 0, 0));
  83. setFoldColor(SC_MARKNUM_FOLDEROPEN, QColor(Qt::white), QColor(128, 128, 128));
  84. setFoldColor(SC_MARKNUM_FOLDER, QColor(Qt::white), QColor(128, 128, 128));
  85. setFoldColor(SC_MARKNUM_FOLDERSUB, QColor(Qt::white), QColor(128, 128, 128));
  86. setFoldColor(SC_MARKNUM_FOLDERTAIL, QColor(Qt::white), QColor(128, 128, 128));
  87. setFoldColor(SC_MARKNUM_FOLDEREND, QColor(Qt::white), QColor(128, 128, 128));
  88. setFoldColor(SC_MARKNUM_FOLDEROPENMID, QColor(Qt::white), QColor(128, 128, 128));
  89. setFoldColor(SC_MARKNUM_FOLDERMIDTAIL, QColor(Qt::white), QColor(128, 128, 128));
  90. }
  91. }
  92. void QsciDisplayWindow::setIsShowFindItem(bool v)
  93. {
  94. m_isShowFindItem = v;
  95. }
  96. sptr_t QsciDisplayWindow::execute(quint32 Msg, uptr_t wParam, sptr_t lParam) const {
  97. try {
  98. return (m_pScintillaFunc) ? m_pScintillaFunc(m_pScintillaPtr, Msg, wParam, lParam) : -1;
  99. }
  100. catch (...)
  101. {
  102. return -1;
  103. }
  104. };
  105. void QsciDisplayWindow::mouseDoubleClickEvent(QMouseEvent * e)
  106. {
  107. QsciScintilla::mouseDoubleClickEvent(e);
  108. if (hasSelectedText())
  109. {
  110. emit delayWork();
  111. }
  112. }
  113. void QsciDisplayWindow::clearIndicator(int indicatorNumber) {
  114. size_t docStart = 0;
  115. size_t docEnd = length();
  116. execute(SCI_SETINDICATORCURRENT, indicatorNumber);
  117. execute(SCI_INDICATORCLEARRANGE, docStart, docEnd - docStart);
  118. };
  119. const int MAXLINEHIGHLIGHT = 400;
  120. void QsciDisplayWindow::slot_clearHightWord()
  121. {
  122. if (m_hasHighlight)
  123. {
  124. m_hasHighlight = false;
  125. clearIndicator(SCE_UNIVERSAL_FOUND_STYLE_SMART);
  126. }
  127. }
  128. void QsciDisplayWindow::highlightViewWithWord(QString & word2Hilite)
  129. {
  130. int originalStartPos = execute(SCI_GETTARGETSTART);
  131. int originalEndPos = execute(SCI_GETTARGETEND);
  132. int firstLine = static_cast<int>(this->execute(SCI_GETFIRSTVISIBLELINE));
  133. int nbLineOnScreen = this->execute(SCI_LINESONSCREEN);
  134. int nbLines = std::min(nbLineOnScreen, MAXLINEHIGHLIGHT) + 1;
  135. int lastLine = firstLine + nbLines;
  136. int startPos = 0;
  137. int endPos = 0;
  138. auto currentLine = firstLine;
  139. int prevDocLineChecked = -1; //invalid start
  140. auto searchMark = [this](int &startPos, int &endPos, QByteArray &word2Mark) {
  141. int targetStart = 0;
  142. int targetEnd = 0;
  143. long lens = word2Mark.length();
  144. while (targetStart >= 0)
  145. {
  146. execute(SCI_SETTARGETRANGE, startPos, endPos);
  147. targetStart = SendScintilla(SCI_SEARCHINTARGET, lens, word2Mark.data());
  148. if (targetStart == -1 || targetStart == -2)
  149. break;
  150. targetEnd = int(this->execute(SCI_GETTARGETEND));
  151. if (targetEnd > endPos)
  152. {
  153. //we found a result but outside our range, therefore do not process it
  154. break;
  155. }
  156. int foundTextLen = targetEnd - targetStart;
  157. if (foundTextLen > 0)
  158. {
  159. this->execute(SCI_SETINDICATORCURRENT, SCE_UNIVERSAL_FOUND_STYLE_SMART);
  160. this->execute(SCI_INDICATORFILLRANGE, targetStart, foundTextLen);
  161. }
  162. if (targetStart + foundTextLen == endPos)
  163. break;
  164. startPos = targetStart + foundTextLen;
  165. }
  166. };
  167. QByteArray whatMark = word2Hilite.toUtf8();
  168. SendScintilla(SCI_SETSEARCHFLAGS, SCFIND_REGEXP | SCFIND_MATCHCASE | SCFIND_WHOLEWORD | SCFIND_REGEXP_SKIPCRLFASONE);
  169. for (; currentLine < lastLine; ++currentLine)
  170. {
  171. int docLine = static_cast<int>(this->execute(SCI_DOCLINEFROMVISIBLE, currentLine));
  172. if (docLine == prevDocLineChecked)
  173. continue; //still on same line (wordwrap)
  174. prevDocLineChecked = docLine;
  175. startPos = static_cast<int>(this->execute(SCI_POSITIONFROMLINE, docLine));
  176. endPos = static_cast<int>(this->execute(SCI_POSITIONFROMLINE, docLine + 1));
  177. if (endPos == -1)
  178. { //past EOF
  179. endPos = this->length() - 1;
  180. searchMark(startPos, endPos, whatMark);
  181. break;
  182. }
  183. else
  184. {
  185. searchMark(startPos, endPos, whatMark);
  186. }
  187. }
  188. m_hasHighlight = true;
  189. // restore the original targets to avoid conflicts with the search/replace functions
  190. this->execute(SCI_SETTARGETRANGE, originalStartPos, originalEndPos);
  191. }
  192. void QsciDisplayWindow::slot_delayWork()
  193. {
  194. if (!hasSelectedText())
  195. {
  196. return;
  197. }
  198. QString word = selectedText();
  199. if (!word.isEmpty())
  200. {
  201. highlightViewWithWord(word);
  202. }
  203. #if 0
  204. if (!hasSelectedText())
  205. {
  206. return;
  207. }
  208. QString word = selectedText();
  209. if (!word.isEmpty())
  210. {
  211. QVector<int>resultPos;
  212. resultPos.reserve(50);
  213. int firstLine = execute(SCI_GETFIRSTVISIBLELINE);
  214. int nbLineOnScreen = execute(SCI_LINESONSCREEN);
  215. int nbLines = std::min(nbLineOnScreen, MAXLINEHIGHLIGHT) + 1;
  216. int lastLine = firstLine + nbLines;
  217. long startPos = execute(SCI_POSITIONFROMLINE, firstLine);
  218. long endPos = execute(SCI_POSITIONFROMLINE, lastLine);
  219. if (endPos == -1)
  220. {
  221. endPos = execute(SCI_GETLENGTH);
  222. }
  223. int curpos = execute(SCI_GETCURRENTPOS);
  224. int mainSelect = 1;
  225. struct Sci_TextToFind findOptions;
  226. findOptions.chrg.cpMin = startPos;
  227. findOptions.chrg.cpMax = endPos;
  228. std::string wordStr = word.toStdString();
  229. findOptions.lpstrText = wordStr.c_str();
  230. int pos = execute(SCI_FINDTEXT, SCFIND_MATCHCASE | SCFIND_WHOLEWORD, reinterpret_cast<sptr_t>(&findOptions));
  231. while (pos != -1)
  232. {
  233. resultPos.append(pos);
  234. if (pos <= curpos)
  235. {
  236. mainSelect = resultPos.size();
  237. }
  238. findOptions.chrg.cpMin = findOptions.chrgText.cpMax;
  239. pos = execute(SCI_FINDTEXT, SCFIND_MATCHCASE | SCFIND_WHOLEWORD, reinterpret_cast<sptr_t>(&findOptions));
  240. }
  241. for (int i = 0, size = resultPos.size(); i < size; ++i)
  242. {
  243. execute(SCI_ADDSELECTION, resultPos.at(i), resultPos.at(i) + word.size());
  244. }
  245. if (!resultPos.isEmpty())
  246. {
  247. execute(SCI_SETMAINSELECTION, mainSelect - 1);
  248. }
  249. }
  250. #endif
  251. }
  252. void QsciDisplayWindow::setMediator(MediatorDisplay* mediator)
  253. {
  254. m_mediator = mediator;
  255. }
  256. //滚动条值变化后的槽函数。一旦滚动则会出发这里,发送消息给中介,让中介去同步另外一方
  257. void QsciDisplayWindow::slot_scrollYValueChange(int value)
  258. {
  259. if (m_direction == RC_LEFT)
  260. {
  261. if (m_mediator->getLeftScrollValue() != value)
  262. {
  263. m_mediator->setLeftScrollValue(value);
  264. }
  265. }
  266. else
  267. {
  268. if (m_mediator->getRightScrollValue() != value)
  269. {
  270. m_mediator->setRightScrollValue(value);
  271. }
  272. }
  273. autoAdjustLineWidth(value);
  274. slot_delayWork();
  275. //qDebug("-- dir s n %d %d", m_direction, contentY());
  276. }
  277. //根据现有滚动条来决定是否更新屏幕线宽长度。每滚动2000个单位必须调整line宽
  278. void QsciDisplayWindow::autoAdjustLineWidth(int xScrollValue)
  279. {
  280. if (std::abs(xScrollValue - m_preFirstLineNum) > 400)
  281. {
  282. m_preFirstLineNum = xScrollValue;
  283. updateLineNumberWidth();
  284. }
  285. }
  286. void QsciDisplayWindow::updateLineNumberWidth()
  287. {
  288. auto linesVisible = execute(SCI_LINESONSCREEN);
  289. if (linesVisible)
  290. {
  291. int nbDigits = 0;
  292. auto firstVisibleLineVis = execute(SCI_GETFIRSTVISIBLELINE);
  293. auto lastVisibleLineVis = linesVisible + firstVisibleLineVis + 1;
  294. auto lastVisibleLineDoc = execute(SCI_DOCLINEFROMVISIBLE, lastVisibleLineVis);
  295. nbDigits = nbDigitsFromNbLines(lastVisibleLineDoc);
  296. nbDigits = nbDigits < 4 ? 4 : nbDigits;
  297. auto pixelWidth = 8 + nbDigits * execute(SCI_TEXTWIDTH, STYLE_LINENUMBER, reinterpret_cast<sptr_t>("8"));
  298. execute(SCI_SETMARGINWIDTHN, MARGIN_LINE_NUM, pixelWidth);
  299. }
  300. }
  301. //X方向滚动条值变化后的槽函数。一旦滚动则会出发这里,发送消息给中介,让中介去同步另外一方
  302. void QsciDisplayWindow::slot_scrollXValueChange(int value)
  303. {
  304. if (m_direction == RC_LEFT)
  305. {
  306. if (m_mediator->getLeftScrollXValue() != value)
  307. {
  308. m_mediator->setLeftScrollXValue(value);
  309. }
  310. }
  311. else
  312. {
  313. if (m_mediator->getRightScrollXValue() != value)
  314. {
  315. m_mediator->setRightScrollXValue(value);
  316. }
  317. }
  318. //qDebug("-- dir s n %d %d", m_direction, contentY());
  319. }
  320. void QsciDisplayWindow::setDirection(RC_DIRECTION direction)
  321. {
  322. m_direction = direction;
  323. }
  324. int QsciDisplayWindow::getCurVerticalScrollValue()
  325. {
  326. return this->verticalScrollBar()->value();
  327. }
  328. void QsciDisplayWindow::contextUserDefineMenuEvent(QMenu* menu)
  329. {
  330. //QAction* action;
  331. if (menu != nullptr)
  332. {
  333. menu->addAction(tr("Find Text"), this, SLOT(slot_findText()));
  334. menu->addAction(tr("Show File in Explorer"), this, SLOT(slot_showFileInExplorer()));
  335. menu->addAction(tr("Save As ..."), this, &QsciDisplayWindow::sign_saveAsFile);
  336. }
  337. menu->show();
  338. }
  339. void QsciDisplayWindow::inputMethodEvent(QInputMethodEvent* event)
  340. {
  341. if (!event->preeditString().isEmpty())
  342. {
  343. return;
  344. }
  345. QsciScintilla::inputMethodEvent(event);
  346. }
  347. void QsciDisplayWindow::slot_findText()
  348. {
  349. if (m_isShowFindItem)
  350. {
  351. if (m_textFindWin == nullptr)
  352. {
  353. m_textFindWin = new TextFind(m_direction);
  354. connect(m_textFindWin, &TextFind::signFindFile, this, &QsciDisplayWindow::slot_FindTextWithPara);
  355. m_textFindWin->activateWindow();
  356. m_textFindWin->show();
  357. }
  358. else
  359. {
  360. m_textFindWin->activateWindow();
  361. m_textFindWin->showNormal();
  362. }
  363. m_findCurPos = 0;
  364. }
  365. else
  366. {
  367. //不使用这里的查找,直接发信号到外面
  368. emit sign_find();
  369. }
  370. }
  371. void QsciDisplayWindow::slot_FindTextWithPara(int prevOrNext, QString text)
  372. {
  373. std::string str = text.toStdString();
  374. int length = SendScintilla(SCI_GETLENGTH);
  375. if (length > 0)
  376. {
  377. if (prevOrNext == 1)
  378. {
  379. SendScintilla(SCI_SETTARGETSTART, m_findCurPos);
  380. SendScintilla(SCI_SETTARGETEND, length);
  381. int ret = SendScintilla(SCI_SEARCHINTARGET, str.length(), str.c_str());
  382. if (ret >= 0)
  383. {
  384. m_findCurPos = ret + str.length();
  385. SendScintilla(SCI_GOTOPOS, ret);
  386. SendScintilla(SCI_SETSELECTION, ret, ret + str.length());
  387. }
  388. else
  389. {
  390. QMessageBox::information(this, tr("Not Find"), tr("Not Find Next!"));
  391. m_textFindWin->activateWindow();
  392. }
  393. }
  394. else if (prevOrNext == 0)
  395. {
  396. SendScintilla(SCI_SETTARGETSTART, m_findCurPos);
  397. SendScintilla(SCI_SETTARGETEND, 0);
  398. int ret = SendScintilla(SCI_SEARCHINTARGET, str.length(), str.c_str());
  399. if (ret >= 0)
  400. {
  401. m_findCurPos = ret - 1;
  402. SendScintilla(SCI_GOTOPOS, ret);
  403. SendScintilla(SCI_SETSELECTION, ret, ret + str.length());
  404. }
  405. else
  406. {
  407. QMessageBox::information(this, tr("Not Find"), tr("Not Find Prev!"));
  408. m_textFindWin->activateWindow();
  409. }
  410. }
  411. }
  412. }
  413. //定位到文件夹
  414. void QsciDisplayWindow::slot_showFileInExplorer()
  415. {
  416. showFileInExplorer(m_filePath);
  417. }