1 | /* |
2 | * Copyright (C) 2020-2022 Roy Qu (royqh1979@gmail.com) |
3 | * |
4 | * This program is free software: you can redistribute it and/or modify |
5 | * it under the terms of the GNU General Public License as published by |
6 | * the Free Software Foundation, either version 3 of the License, or |
7 | * (at your option) any later version. |
8 | * |
9 | * This program is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 | * GNU General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU General Public License |
15 | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
16 | */ |
17 | #include "qconsole.h" |
18 | |
19 | #include <QEvent> |
20 | #include <QInputMethodEvent> |
21 | #include <QKeyEvent> |
22 | #include <QPaintEvent> |
23 | #include <QPainter> |
24 | #include <QRect> |
25 | #include <QScrollBar> |
26 | #include <cmath> |
27 | #include <QDebug> |
28 | #include <QTimer> |
29 | #include <QApplication> |
30 | #include <QClipboard> |
31 | #include "../utils.h" |
32 | |
33 | QConsole::QConsole(QWidget *parent): |
34 | QAbstractScrollArea(parent), |
35 | mContents(this), |
36 | mContentImage() |
37 | { |
38 | mHistorySize = 500; |
39 | mHistoryIndex = -1; |
40 | mCommand = "" ; |
41 | mCurrentEditableLine = "" ; |
42 | mRowHeight = 0; |
43 | mTopRow = 1; |
44 | mRowsInWindow = 0; |
45 | mColumnsPerRow = 0; |
46 | mColumnWidth = 0; |
47 | mReadonly = false; |
48 | mSelectionBegin = {0,0}; |
49 | mSelectionEnd = {0,0}; |
50 | mCaretChar = 0; |
51 | mBackground = palette().color(QPalette::Base); |
52 | mForeground = palette().color(QPalette::Text); |
53 | mSelectionBackground = palette().color(QPalette::Highlight); |
54 | mSelectionForeground = palette().color(QPalette::HighlightedText); |
55 | mInactiveSelectionBackground = palette().color(QPalette::Inactive,QPalette::Highlight); |
56 | mInactiveSelectionForeground = palette().color(QPalette::Inactive,QPalette::HighlightedText); |
57 | mTabSize = 4; |
58 | mBlinkTimerId = 0; |
59 | mBlinkStatus = 0; |
60 | //enable input method |
61 | setAttribute(Qt::WA_InputMethodEnabled); |
62 | // setMouseTracking(false); |
63 | recalcCharExtent(); |
64 | mScrollTimer = new QTimer(this); |
65 | mScrollTimer->setInterval(100); |
66 | connect(mScrollTimer,&QTimer::timeout,this, &QConsole::scrollTimerHandler); |
67 | connect(&mContents,&ConsoleLines::layoutFinished,this, &QConsole::contentsLayouted); |
68 | connect(&mContents,&ConsoleLines::rowsAdded,this, &QConsole::contentsRowsAdded); |
69 | connect(&mContents,&ConsoleLines::lastRowsChanged,this, &QConsole::contentsLastRowsChanged); |
70 | connect(&mContents,&ConsoleLines::lastRowsRemoved,this, &QConsole::contentsLastRowsRemoved); |
71 | connect(verticalScrollBar(),&QScrollBar::valueChanged, |
72 | this, &QConsole::doScrolled); |
73 | |
74 | } |
75 | |
76 | int QConsole::historySize() const |
77 | { |
78 | return mHistorySize; |
79 | } |
80 | |
81 | void QConsole::setHistorySize(int historySize) |
82 | { |
83 | mHistorySize = historySize; |
84 | } |
85 | |
86 | int QConsole::tabSize() const |
87 | { |
88 | return mTabSize; |
89 | } |
90 | |
91 | int QConsole::columnsPerRow() const |
92 | { |
93 | return mColumnsPerRow; |
94 | } |
95 | |
96 | int QConsole::rowsInWindow() const |
97 | { |
98 | return mRowsInWindow; |
99 | } |
100 | |
101 | int QConsole::charColumns(QChar ch, int columnsBefore) const |
102 | { |
103 | if (ch == '\t') { |
104 | return mTabSize - (columnsBefore % mTabSize); |
105 | } |
106 | if (ch == ' ') |
107 | return 1; |
108 | return std::ceil((int)(fontMetrics().horizontalAdvance(ch)) / (double) mColumnWidth); |
109 | } |
110 | |
111 | void QConsole::invalidate() |
112 | { |
113 | viewport()->update(); |
114 | } |
115 | |
116 | void QConsole::invalidateRows(int startRow, int endRow) |
117 | { |
118 | if (!isVisible()) |
119 | return; |
120 | if (startRow == -1 && endRow == -1) { |
121 | invalidate(); |
122 | } else { |
123 | startRow = std::max(startRow, 1); |
124 | endRow = std::max(endRow, 1); |
125 | // find the visible lines first |
126 | if (startRow > endRow) |
127 | std::swap(startRow, endRow); |
128 | |
129 | if (endRow >= mContents.rows()) |
130 | endRow = INT_MAX; // paint empty space beyond last line |
131 | |
132 | // mTopLine is in display coordinates, so FirstLine and LastLine must be |
133 | // converted previously. |
134 | startRow = std::max(startRow, mTopRow); |
135 | endRow = std::min(endRow, mTopRow + mRowsInWindow-1); |
136 | |
137 | // any line visible? |
138 | if (endRow >= startRow) { |
139 | QRect rcInval = { |
140 | 0, |
141 | mRowHeight * (startRow - mTopRow), |
142 | clientWidth(), mRowHeight * (endRow - mTopRow + 1) |
143 | }; |
144 | invalidateRect(rcInval); |
145 | } |
146 | } |
147 | |
148 | } |
149 | |
150 | void QConsole::invalidateRect(const QRect &rect) |
151 | { |
152 | viewport()->update(rect); |
153 | } |
154 | |
155 | void QConsole::addLine(const QString &line) |
156 | { |
157 | mCurrentEditableLine = "" ; |
158 | mCaretChar=0; |
159 | mSelectionBegin = caretPos(); |
160 | mSelectionEnd = caretPos(); |
161 | mContents.addLine(line); |
162 | } |
163 | |
164 | void QConsole::addText(const QString &text) |
165 | { |
166 | QStringList lst = textToLines(text); |
167 | for (const QString& line:lst) { |
168 | addLine(line); |
169 | } |
170 | } |
171 | |
172 | void QConsole::removeLastLine() |
173 | { |
174 | mCurrentEditableLine = "" ; |
175 | mCaretChar=0; |
176 | mSelectionBegin = caretPos(); |
177 | mSelectionEnd = caretPos(); |
178 | mContents.RemoveLastLine(); |
179 | } |
180 | |
181 | void QConsole::changeLastLine(const QString &line) |
182 | { |
183 | mContents.changeLastLine(line); |
184 | } |
185 | |
186 | QString QConsole::getLastLine() |
187 | { |
188 | return mContents.getLastLine(); |
189 | } |
190 | |
191 | void QConsole::clear() |
192 | { |
193 | mContents.clear(); |
194 | mCommand = "" ; |
195 | mCurrentEditableLine = "" ; |
196 | mTopRow = 1; |
197 | mSelectionBegin = {0,0}; |
198 | mSelectionEnd = {0,0}; |
199 | mCaretChar = 0; |
200 | updateScrollbars(); |
201 | } |
202 | |
203 | void QConsole::copy() |
204 | { |
205 | if (!this->hasSelection()) |
206 | return; |
207 | QString s = selText(); |
208 | QClipboard* clipboard=QGuiApplication::clipboard(); |
209 | clipboard->clear(); |
210 | clipboard->setText(s); |
211 | } |
212 | |
213 | void QConsole::paste() |
214 | { |
215 | if (mReadonly) |
216 | return; |
217 | QClipboard* clipboard=QGuiApplication::clipboard(); |
218 | textInputed(clipboard->text()); |
219 | } |
220 | |
221 | void QConsole::selectAll() |
222 | { |
223 | if (mContents.lines()>0) { |
224 | mSelectionBegin = {1,1}; |
225 | mSelectionEnd = { mContents.getLastLine().length()+1,mContents.lines()}; |
226 | } |
227 | } |
228 | |
229 | QString QConsole::selText() |
230 | { |
231 | if (!hasSelection()) |
232 | return "" ; |
233 | int ColFrom = selectionBegin().ch; |
234 | int First = selectionBegin().line; |
235 | int ColTo = selectionEnd().ch; |
236 | int Last = selectionEnd().line; |
237 | if (First == Last) { |
238 | QString s = mContents.getLine(First); |
239 | if (First == mContents.lines()) { |
240 | s += this->mCommand; |
241 | } |
242 | return s.mid(ColFrom, ColTo - ColFrom); |
243 | |
244 | } else { |
245 | QString result = mContents.getLine(First).mid(ColFrom); |
246 | result+= lineBreak(); |
247 | for (int i = First + 1; i<=Last - 1; i++) { |
248 | result += mContents.getLine(i); |
249 | result+= lineBreak(); |
250 | } |
251 | QString s = mContents.getLine(Last); |
252 | if (Last == mContents.lines()) |
253 | s+= this->mCommand; |
254 | result += s.leftRef(ColTo); |
255 | return result; |
256 | } |
257 | } |
258 | |
259 | void QConsole::recalcCharExtent() { |
260 | mRowHeight = fontMetrics().lineSpacing(); |
261 | mColumnWidth = fontMetrics().horizontalAdvance("M" ); |
262 | } |
263 | |
264 | void QConsole::sizeOrFontChanged(bool) |
265 | { |
266 | if (mColumnWidth != 0) { |
267 | mColumnsPerRow = std::max(clientWidth()-2,0) / mColumnWidth; |
268 | mRowsInWindow = clientHeight() / mRowHeight; |
269 | mContents.layout(); |
270 | } |
271 | |
272 | } |
273 | |
274 | int QConsole::clientWidth() |
275 | { |
276 | return viewport()->size().width(); |
277 | } |
278 | |
279 | int QConsole::clientHeight() |
280 | { |
281 | return viewport()->size().height(); |
282 | } |
283 | |
284 | void QConsole::setTopRow(int value) |
285 | { |
286 | value = std::min(value,maxScrollHeight()); |
287 | value = std::max(value, 1); |
288 | if (value != mTopRow) { |
289 | verticalScrollBar()->setValue(value); |
290 | } |
291 | } |
292 | |
293 | int QConsole::maxScrollHeight() |
294 | { |
295 | return std::max(mContents.rows()-mRowsInWindow+1,1); |
296 | } |
297 | |
298 | void QConsole::updateScrollbars() |
299 | { |
300 | setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAlwaysOff); |
301 | setVerticalScrollBarPolicy(Qt::ScrollBarPolicy::ScrollBarAsNeeded); |
302 | int nMaxScroll = maxScrollHeight(); |
303 | int nMin = 1; |
304 | int nMax = std::max(1, nMaxScroll); |
305 | int nPage = mRowsInWindow; |
306 | int nPos = std::min(std::max(mTopRow,nMin),nMax); |
307 | verticalScrollBar()->setMinimum(nMin); |
308 | verticalScrollBar()->setMaximum(nMax); |
309 | verticalScrollBar()->setPageStep(nPage); |
310 | verticalScrollBar()->setSingleStep(1); |
311 | if (nPos != verticalScrollBar()->value()) |
312 | verticalScrollBar()->setValue(nPos); |
313 | else |
314 | invalidate(); |
315 | } |
316 | |
317 | void QConsole::paintRows(QPainter &painter, int row1, int row2) |
318 | { |
319 | if (row1>row2) |
320 | return; |
321 | QRect rect(0,(row1-mTopRow)*mRowHeight,clientWidth(),(row2-row1+1)*mRowHeight); |
322 | painter.fillRect(rect,mBackground); |
323 | QStringList lst = mContents.getRows(row1,row2); |
324 | int startRow = row1-mTopRow; |
325 | painter.setPen(mForeground); |
326 | RowColumn selBeginRC = mContents.lineCharToRowColumn(selectionBegin()); |
327 | RowColumn selEndRC = mContents.lineCharToRowColumn(selectionEnd()); |
328 | LineChar editBegin = { |
329 | mContents.getLastLine().length() - mCurrentEditableLine.length(), |
330 | mContents.lines()-1 |
331 | }; |
332 | RowColumn editBeginRC = mContents.lineCharToRowColumn(editBegin); |
333 | bool isSelection = false; |
334 | painter.setPen(mForeground); |
335 | for (int i=0; i< lst.size(); i++) { |
336 | int currentRow = i+row1-1; |
337 | int left=2; |
338 | int top = (startRow+i) * mRowHeight; |
339 | int baseLine = (startRow+i+1)*mRowHeight - painter.fontMetrics().descent(); |
340 | QString s = lst[i]; |
341 | int columnsBefore = 0; |
342 | for (QChar ch:s) { |
343 | int charCol = charColumns(ch,columnsBefore); |
344 | int width = charCol * mColumnWidth; |
345 | if ((currentRow > selBeginRC.row || |
346 | (currentRow == selBeginRC.row && columnsBefore>=selBeginRC.column)) |
347 | && |
348 | (currentRow < selEndRC.row || |
349 | (currentRow == selEndRC.row && columnsBefore+charCol<=selEndRC.column))) { |
350 | if (!isSelection) { |
351 | isSelection = true; |
352 | } |
353 | if (!mReadonly &&(currentRow>editBeginRC.row || |
354 | columnsBefore >= editBeginRC.column)) { |
355 | painter.setPen(mSelectionForeground); |
356 | painter.fillRect(left,top,width,mRowHeight,mSelectionBackground); |
357 | } else { |
358 | painter.setPen(mInactiveSelectionForeground); |
359 | painter.fillRect(left,top,width,mRowHeight,mInactiveSelectionBackground); |
360 | } |
361 | } else { |
362 | if (isSelection) { |
363 | isSelection = false; |
364 | painter.setPen(mForeground); |
365 | } |
366 | } |
367 | painter.drawText(left,baseLine,ch); |
368 | left+= width; |
369 | columnsBefore += charCol; |
370 | } |
371 | } |
372 | } |
373 | |
374 | void QConsole::ensureCaretVisible() |
375 | { |
376 | int caretRow = mContents.rows(); |
377 | if (caretRow < mTopRow) { |
378 | mTopRow = caretRow; |
379 | return; |
380 | } |
381 | if (caretRow >= mTopRow + mRowsInWindow) { |
382 | mTopRow = caretRow + 1 -mRowsInWindow; |
383 | } |
384 | } |
385 | |
386 | void QConsole::showCaret() |
387 | { |
388 | if (mBlinkTimerId==0) |
389 | mBlinkTimerId = startTimer(500); |
390 | } |
391 | |
392 | void QConsole::hideCaret() |
393 | { |
394 | if (mBlinkTimerId!=0) { |
395 | killTimer(mBlinkTimerId); |
396 | mBlinkTimerId = 0; |
397 | mBlinkStatus = 0; |
398 | updateCaret(); |
399 | } |
400 | } |
401 | |
402 | void QConsole::updateCaret() |
403 | { |
404 | QRect rcCaret = getCaretRect(); |
405 | invalidateRect(rcCaret); |
406 | } |
407 | |
408 | LineChar QConsole::caretPos() |
409 | { |
410 | QString lastLine = mContents.getLastLine(); |
411 | int line = std::max(mContents.lines()-1,0); |
412 | int charIndex = 0; |
413 | if (mCaretChar>=mCurrentEditableLine.length()) { |
414 | charIndex = lastLine.length(); |
415 | } else { |
416 | charIndex = lastLine.length()-mCurrentEditableLine.length()+mCaretChar; |
417 | } |
418 | return {charIndex,line}; |
419 | } |
420 | |
421 | RowColumn QConsole::caretRowColumn() |
422 | { |
423 | return mContents.lineCharToRowColumn(caretPos()); |
424 | } |
425 | |
426 | QPoint QConsole::rowColumnToPixels(const RowColumn &rowColumn) |
427 | { |
428 | /* |
429 | mTopRow is 1-based; rowColumn.row is 0-based |
430 | */ |
431 | int row =rowColumn.row+1 - mTopRow; |
432 | int col =rowColumn.column; |
433 | return QPoint(2+col*mColumnWidth, row*mRowHeight); |
434 | } |
435 | |
436 | QRect QConsole::getCaretRect() |
437 | { |
438 | LineChar caret = caretPos(); |
439 | QChar caretChar = mContents.getChar(caret); |
440 | RowColumn caretRC = mContents.lineCharToRowColumn(caret); |
441 | QPoint caretPos = rowColumnToPixels(caretRC); |
442 | int caretWidth=mColumnWidth; |
443 | //qDebug()<<"caret"<<mCaretX<<mCaretY; |
444 | int columnsBefore = caretRC.column; |
445 | if (!caretChar.isNull()) { |
446 | caretWidth = charColumns(caretChar, columnsBefore)*mColumnWidth; |
447 | } |
448 | return QRect(caretPos.x(),caretPos.y(),caretWidth, |
449 | mRowHeight); |
450 | } |
451 | |
452 | void QConsole::doScrolled() |
453 | { |
454 | mTopRow = verticalScrollBar()->value(); |
455 | invalidate(); |
456 | } |
457 | |
458 | void QConsole::contentsLayouted() |
459 | { |
460 | updateScrollbars(); |
461 | } |
462 | |
463 | void QConsole::contentsRowsAdded(int ) |
464 | { |
465 | ensureCaretVisible(); |
466 | updateScrollbars(); |
467 | } |
468 | |
469 | void QConsole::contentsLastRowsRemoved(int ) |
470 | { |
471 | ensureCaretVisible(); |
472 | updateScrollbars(); |
473 | } |
474 | |
475 | void QConsole::contentsLastRowsChanged(int rowCount) |
476 | { |
477 | ensureCaretVisible(); |
478 | invalidateRows(mContents.rows()-rowCount+1,mContents.rows()); |
479 | } |
480 | |
481 | void QConsole::scrollTimerHandler() |
482 | |
483 | { |
484 | QPoint iMousePos; |
485 | |
486 | iMousePos = QCursor::pos(); |
487 | iMousePos = mapFromGlobal(iMousePos); |
488 | RowColumn mousePosRC = pixelsToNearestRowColumn(iMousePos.x(),iMousePos.y()); |
489 | |
490 | if (mScrollDeltaY != 0) { |
491 | qDebug()<<"scroll timer" <<mScrollDeltaY; |
492 | qDebug()<<mousePosRC.row<<mousePosRC.column; |
493 | if (QApplication::queryKeyboardModifiers().testFlag(Qt::ShiftModifier)) |
494 | setTopRow(mTopRow + mScrollDeltaY * mRowsInWindow); |
495 | else |
496 | setTopRow(mTopRow + mScrollDeltaY); |
497 | int row = mTopRow; |
498 | if (mScrollDeltaY > 0) // scrolling down? |
499 | row+=mRowsInWindow - 1; |
500 | mousePosRC.row = row - 1; |
501 | qDebug()<<row; |
502 | int oldStartRow = mContents.lineCharToRowColumn(selectionBegin()).row+1; |
503 | int oldEndRow = mContents.lineCharToRowColumn(selectionEnd()).row+1; |
504 | invalidateRows(oldStartRow,oldEndRow); |
505 | mSelectionEnd = mContents.rowColumnToLineChar(mousePosRC); |
506 | invalidateRows(row,row); |
507 | } |
508 | |
509 | // computeScrollY(Y); |
510 | } |
511 | |
512 | void QConsole::mousePressEvent(QMouseEvent *event) |
513 | { |
514 | Qt::MouseButton button = event->button(); |
515 | int X=event->pos().x(); |
516 | int Y=event->pos().y(); |
517 | |
518 | QAbstractScrollArea::mousePressEvent(event); |
519 | |
520 | //fKbdHandler.ExecuteMouseDown(Self, Button, Shift, X, Y); |
521 | |
522 | if (button == Qt::LeftButton) { |
523 | // setMouseTracking(true); |
524 | RowColumn mousePosRC = pixelsToNearestRowColumn(X,Y); |
525 | LineChar mousePos = mContents.rowColumnToLineChar(mousePosRC); |
526 | //I couldn't track down why, but sometimes (and definitely not all the time) |
527 | //the block positioning is lost. This makes sure that the block is |
528 | //maintained in case they started a drag operation on the block |
529 | int oldStartRow = mContents.lineCharToRowColumn(selectionBegin()).row+1; |
530 | int oldEndRow = mContents.lineCharToRowColumn(selectionEnd()).row+1; |
531 | invalidateRows(oldStartRow,oldEndRow); |
532 | mSelectionBegin = mousePos; |
533 | mSelectionEnd = mousePos; |
534 | } |
535 | } |
536 | |
537 | void QConsole::mouseReleaseEvent(QMouseEvent *event) |
538 | { |
539 | QAbstractScrollArea::mouseReleaseEvent(event); |
540 | mScrollTimer->stop(); |
541 | // setMouseTracking(false); |
542 | |
543 | } |
544 | |
545 | void QConsole::mouseMoveEvent(QMouseEvent *event) |
546 | { |
547 | QAbstractScrollArea::mouseMoveEvent(event); |
548 | Qt::MouseButtons buttons = event->buttons(); |
549 | int X=event->pos().x(); |
550 | int Y=event->pos().y(); |
551 | |
552 | if ((buttons == Qt::LeftButton)) { |
553 | // should we begin scrolling? |
554 | computeScrollY(Y); |
555 | RowColumn mousePosRC = pixelsToNearestRowColumn(X, Y); |
556 | LineChar mousePos = mContents.rowColumnToLineChar(mousePosRC); |
557 | if (mScrollDeltaY == 0) { |
558 | int oldStartRow = mContents.lineCharToRowColumn(selectionBegin()).row+1; |
559 | int oldEndRow = mContents.lineCharToRowColumn(selectionEnd()).row+1; |
560 | invalidateRows(oldStartRow,oldEndRow); |
561 | mSelectionEnd = mousePos; |
562 | int row = mContents.lineCharToRowColumn(mSelectionEnd).row+1; |
563 | invalidateRows(row,row); |
564 | } |
565 | } |
566 | } |
567 | |
568 | void QConsole::keyPressEvent(QKeyEvent *event) |
569 | { |
570 | switch(event->key()) { |
571 | case Qt::Key_Return: |
572 | case Qt::Key_Enter: |
573 | event->accept(); |
574 | if (mReadonly) |
575 | return; |
576 | emit commandInput(mCommand); |
577 | if (mHistorySize>0) { |
578 | if (mCommandHistory.size()==mHistorySize) { |
579 | mCommandHistory.pop_front(); |
580 | mHistoryIndex--; |
581 | } |
582 | if (mCommandHistory.size()==0 || mHistoryIndex<0 || mHistoryIndex == mCommandHistory.size()-1) { |
583 | mHistoryIndex=mCommandHistory.size(); |
584 | } |
585 | mCommandHistory.append(mCommand); |
586 | } |
587 | mCommand="" ; |
588 | addLine("" ); |
589 | return; |
590 | case Qt::Key_Up: |
591 | event->accept(); |
592 | if (mHistorySize>0 && mHistoryIndex>0) { |
593 | mHistoryIndex--; |
594 | loadCommandFromHistory(); |
595 | } |
596 | return; |
597 | case Qt::Key_Down: |
598 | event->accept(); |
599 | if (mHistorySize>0 && mHistoryIndex<mCommandHistory.size()-1) { |
600 | mHistoryIndex++; |
601 | loadCommandFromHistory(); |
602 | } |
603 | return; |
604 | case Qt::Key_Left: |
605 | event->accept(); |
606 | if (mReadonly) |
607 | return; |
608 | if (mCaretChar>0 && mCaretChar<=mCurrentEditableLine.size()) { |
609 | setCaretChar(mCaretChar-1, !(event->modifiers() & Qt::ShiftModifier)); |
610 | } |
611 | return; |
612 | case Qt::Key_Right: |
613 | event->accept(); |
614 | if (mReadonly) |
615 | return; |
616 | if (mCaretChar<mCurrentEditableLine.size()) { |
617 | setCaretChar(mCaretChar+1, !(event->modifiers() & Qt::ShiftModifier)); |
618 | } |
619 | return; |
620 | case Qt::Key_Home: |
621 | event->accept(); |
622 | if (mReadonly) |
623 | return; |
624 | if (mCaretChar>0 && mCaretChar<=mCurrentEditableLine.size()) { |
625 | setCaretChar(0, !(event->modifiers() & Qt::ShiftModifier)); |
626 | } |
627 | return; |
628 | case Qt::Key_End: |
629 | event->accept(); |
630 | if (mReadonly) |
631 | return; |
632 | if (mCaretChar<mCurrentEditableLine.size()) { |
633 | setCaretChar(mCurrentEditableLine.size(), !(event->modifiers() & Qt::ShiftModifier)); |
634 | } |
635 | return; |
636 | case Qt::Key_Backspace: |
637 | event->accept(); |
638 | if (mReadonly) |
639 | return; |
640 | if (!mCurrentEditableLine.isEmpty() && (mCaretChar-1)<mCurrentEditableLine.size() |
641 | &&(mCaretChar-1>=0)) { |
642 | QString lastLine; |
643 | if (caretInSelection()) { |
644 | lastLine = removeSelection(); |
645 | } else { |
646 | lastLine = mContents.getLastLine(); |
647 | int len=mCurrentEditableLine.length(); |
648 | mCaretChar--; |
649 | mCommand.remove(mCommand.length()-len+mCaretChar,1); |
650 | lastLine.remove(lastLine.length()-len+mCaretChar,1); |
651 | mCurrentEditableLine.remove(mCaretChar,1); |
652 | } |
653 | mContents.changeLastLine(lastLine); |
654 | mSelectionBegin = caretPos(); |
655 | mSelectionEnd = caretPos(); |
656 | } else { |
657 | if (hasSelection()) { |
658 | mSelectionBegin = caretPos(); |
659 | mSelectionEnd = caretPos(); |
660 | invalidate(); |
661 | } |
662 | } |
663 | return; |
664 | case Qt::Key_Delete: |
665 | event->accept(); |
666 | if (mReadonly) |
667 | return; |
668 | if (!mCurrentEditableLine.isEmpty() && (mCaretChar)<mCurrentEditableLine.size() |
669 | &&(mCaretChar>=0)) { |
670 | QString lastLine; |
671 | if (caretInSelection()) { |
672 | lastLine = removeSelection(); |
673 | } else { |
674 | lastLine = mContents.getLastLine(); |
675 | int len=mCurrentEditableLine.length(); |
676 | mCommand.remove(mCommand.length()-len+mCaretChar,1); |
677 | lastLine.remove(lastLine.length()-len+mCaretChar,1); |
678 | mCurrentEditableLine.remove(mCaretChar,1); |
679 | } |
680 | mContents.changeLastLine(lastLine); |
681 | mSelectionBegin = caretPos(); |
682 | mSelectionEnd = caretPos(); |
683 | } else { |
684 | if (hasSelection()) { |
685 | mSelectionBegin = caretPos(); |
686 | mSelectionEnd = caretPos(); |
687 | invalidate(); |
688 | } |
689 | } |
690 | return; |
691 | default: |
692 | if (!event->text().isEmpty()) { |
693 | event->accept(); |
694 | if (mReadonly) |
695 | return; |
696 | textInputed(event->text()); |
697 | return; |
698 | } |
699 | } |
700 | QAbstractScrollArea::keyPressEvent(event); |
701 | } |
702 | |
703 | void QConsole::focusInEvent(QFocusEvent *) |
704 | { |
705 | showCaret(); |
706 | } |
707 | |
708 | void QConsole::focusOutEvent(QFocusEvent *) |
709 | { |
710 | hideCaret(); |
711 | } |
712 | |
713 | void QConsole::paintEvent(QPaintEvent *event) |
714 | { |
715 | if (mRowHeight==0) |
716 | return; |
717 | // Now paint everything while the caret is hidden. |
718 | QPainter painter(viewport()); |
719 | //Get the invalidated rect. |
720 | QRect rcClip = event->rect(); |
721 | QRect rcCaret= getCaretRect(); |
722 | |
723 | if (rcCaret == rcClip) { |
724 | // only update caret |
725 | painter.drawImage(rcCaret,*mContentImage,rcCaret); |
726 | } else { |
727 | int nL1, nL2; |
728 | // Compute the invalid area in lines |
729 | // lines |
730 | nL1 = std::min(std::max(mTopRow + rcClip.top() / mRowHeight, mTopRow), maxScrollHeight() + mRowsInWindow - 1 ); |
731 | nL2 = std::min(std::max(mTopRow + (rcClip.bottom() + mRowHeight - 1) / mRowHeight, 1), maxScrollHeight() + mRowsInWindow - 1); |
732 | QPainter cachePainter(mContentImage.get()); |
733 | cachePainter.setFont(font()); |
734 | if (viewport()->rect() == rcClip) { |
735 | painter.fillRect(rcClip, mBackground); |
736 | } |
737 | paintRows(cachePainter,nL1,nL2); |
738 | painter.drawImage(rcClip,*mContentImage,rcClip); |
739 | } |
740 | paintCaret(painter, rcCaret); |
741 | } |
742 | |
743 | void QConsole::paintCaret(QPainter &painter, const QRect rcClip) |
744 | { |
745 | if (mBlinkStatus!=1) |
746 | return; |
747 | painter.setClipRect(rcClip); |
748 | ConsoleCaretType ct = ConsoleCaretType::ctHorizontalLine; |
749 | QColor caretColor = mForeground; |
750 | switch(ct) { |
751 | case ConsoleCaretType::ctVerticalLine: |
752 | painter.fillRect(rcClip.left()+1,rcClip.top(),rcClip.left()+2,rcClip.bottom(),caretColor); |
753 | break; |
754 | case ConsoleCaretType::ctHorizontalLine: |
755 | painter.fillRect(rcClip.left(),rcClip.bottom()-2,rcClip.right(),rcClip.bottom()-1,caretColor); |
756 | break; |
757 | case ConsoleCaretType::ctBlock: |
758 | painter.fillRect(rcClip, caretColor); |
759 | break; |
760 | case ConsoleCaretType::ctHalfBlock: |
761 | QRect rc=rcClip; |
762 | rc.setTop(rcClip.top()+rcClip.height() / 2); |
763 | painter.fillRect(rcClip, caretColor); |
764 | break; |
765 | } |
766 | } |
767 | |
768 | void QConsole::textInputed(const QString &text) |
769 | { |
770 | if (mContents.rows()<=0) { |
771 | mContents.addLine("" ); |
772 | } |
773 | QString lastLine; |
774 | if (caretInSelection()) { |
775 | lastLine = removeSelection(); |
776 | } else { |
777 | lastLine = mContents.getLastLine(); |
778 | } |
779 | if (mCaretChar>=mCurrentEditableLine.size()) { |
780 | mCommand += text; |
781 | mCurrentEditableLine += text; |
782 | mCaretChar=mCurrentEditableLine.size(); |
783 | mContents.changeLastLine(lastLine+text); |
784 | } else { |
785 | int len=mCurrentEditableLine.length(); |
786 | mCommand.insert(mCommand.length()-len+mCaretChar,text); |
787 | lastLine.insert(lastLine.length()-len+mCaretChar,text); |
788 | mCurrentEditableLine.insert(mCaretChar,text); |
789 | mContents.changeLastLine(lastLine); |
790 | mCaretChar+=text.length(); |
791 | } |
792 | mSelectionBegin = caretPos(); |
793 | mSelectionEnd = caretPos(); |
794 | } |
795 | |
796 | void QConsole::loadCommandFromHistory() |
797 | { |
798 | if (mHistorySize<=0) |
799 | return; |
800 | mCommand = mCommandHistory[mHistoryIndex]; |
801 | QString lastLine = mContents.getLastLine(); |
802 | int len=mCurrentEditableLine.length(); |
803 | lastLine.remove(lastLine.length()-len,INT_MAX); |
804 | mCurrentEditableLine=mCommand; |
805 | mCaretChar = mCurrentEditableLine.length(); |
806 | mSelectionBegin = caretPos(); |
807 | mSelectionEnd = caretPos(); |
808 | mContents.changeLastLine(lastLine + mCommand); |
809 | } |
810 | |
811 | LineChar QConsole::selectionBegin() |
812 | { |
813 | if (mSelectionBegin.line < mSelectionEnd.line || |
814 | (mSelectionBegin.line == mSelectionEnd.line && |
815 | mSelectionBegin.ch < mSelectionEnd.ch)) |
816 | return mSelectionBegin; |
817 | return mSelectionEnd; |
818 | } |
819 | |
820 | LineChar QConsole::selectionEnd() |
821 | { |
822 | if (mSelectionBegin.line < mSelectionEnd.line || |
823 | (mSelectionBegin.line == mSelectionEnd.line && |
824 | mSelectionBegin.ch < mSelectionEnd.ch)) |
825 | return mSelectionEnd; |
826 | return mSelectionBegin; |
827 | } |
828 | |
829 | void QConsole::setCaretChar(int newCaretChar, bool resetSelection) |
830 | { |
831 | RowColumn oldPosRC = caretRowColumn(); |
832 | RowColumn oldSelBegin = mContents.lineCharToRowColumn(selectionBegin()); |
833 | RowColumn oldSelEnd = mContents.lineCharToRowColumn(selectionEnd()); |
834 | int oldStartRow = std::min(std::min(oldPosRC.row,oldSelBegin.row),oldSelEnd.row); |
835 | int oldEndRow = std::max(std::max(oldPosRC.row,oldSelBegin.row),oldSelEnd.row); |
836 | mCaretChar = newCaretChar; |
837 | LineChar newPos = caretPos(); |
838 | RowColumn newPosRC = mContents.lineCharToRowColumn(newPos); |
839 | if (resetSelection) |
840 | mSelectionBegin = newPos; |
841 | mSelectionEnd = newPos; |
842 | |
843 | int startRow = std::min(newPosRC.row, oldStartRow)+1; |
844 | int endRow = std::max(newPosRC.row, oldEndRow)+1; |
845 | invalidateRows(startRow, endRow); |
846 | } |
847 | |
848 | bool QConsole::caretInSelection() |
849 | { |
850 | if (!hasSelection()) |
851 | return false; |
852 | //LineChar selBegin = selectionBegin(); |
853 | LineChar selEnd = selectionEnd(); |
854 | QString lastline = mContents.getLastLine(); |
855 | int editBeginChar = lastline.length() - mCurrentEditableLine.length(); |
856 | if (selEnd.line == mContents.lines()-1 && selEnd.ch > editBeginChar ) { |
857 | return true; |
858 | } |
859 | return false; |
860 | } |
861 | |
862 | QString QConsole::removeSelection() |
863 | { |
864 | |
865 | QString lastLine = mContents.getLastLine(); |
866 | int len=mCurrentEditableLine.length(); |
867 | LineChar selBegin = selectionBegin(); |
868 | LineChar selEnd = selectionEnd(); |
869 | int selLen = selEnd.ch -selBegin.ch; |
870 | int ch = selBegin.ch -(lastLine.length()-len); |
871 | if (selBegin.line < mContents.lines()-1) { |
872 | mCaretChar = 0; |
873 | selLen = selEnd.ch - (lastLine.length()-len); |
874 | } else if (ch<0) { |
875 | mCaretChar = 0; |
876 | selLen = selLen + ch; |
877 | } else { |
878 | mCaretChar=ch; |
879 | } |
880 | mCommand.remove(mCommand.length()-len+mCaretChar,selLen); |
881 | lastLine.remove(lastLine.length()-len+mCaretChar,selLen); |
882 | mCurrentEditableLine.remove(mCaretChar,selLen); |
883 | return lastLine; |
884 | } |
885 | |
886 | bool QConsole::hasSelection() |
887 | { |
888 | return (mSelectionBegin.line != mSelectionEnd.line) |
889 | || (mSelectionBegin.ch != mSelectionEnd.ch); |
890 | } |
891 | |
892 | int QConsole::computeScrollY(int Y) |
893 | { |
894 | QRect iScrollBounds = viewport()->rect(); |
895 | if (Y < iScrollBounds.top()) |
896 | mScrollDeltaY = (Y - iScrollBounds.top()) / mRowHeight - 1; |
897 | else if (Y >= iScrollBounds.bottom()) |
898 | mScrollDeltaY = (Y - iScrollBounds.bottom()) / mRowHeight + 1; |
899 | else |
900 | mScrollDeltaY = 0; |
901 | |
902 | if (mScrollDeltaY) |
903 | mScrollTimer->start(); |
904 | return mScrollDeltaY; |
905 | } |
906 | |
907 | RowColumn QConsole::pixelsToNearestRowColumn(int x, int y) |
908 | { |
909 | // Result is in display coordinates |
910 | // don't return a partially visible last line |
911 | if (y >= mRowsInWindow * mRowHeight) { |
912 | y = mRowsInWindow * mRowHeight - 1; |
913 | if (y < 0) |
914 | y = 0; |
915 | } |
916 | return { |
917 | std::max(0, (x - 2) / mColumnWidth), |
918 | mTopRow + (y / mRowHeight)-1 |
919 | }; |
920 | } |
921 | |
922 | QString QConsole::lineBreak() |
923 | { |
924 | return "\r\n" ; |
925 | } |
926 | |
927 | |
928 | void QConsole::fontChanged() |
929 | { |
930 | recalcCharExtent(); |
931 | sizeOrFontChanged(true); |
932 | } |
933 | |
934 | bool QConsole::event(QEvent *event) |
935 | { |
936 | switch(event->type()) { |
937 | case QEvent::FontChange: |
938 | fontChanged(); |
939 | break; |
940 | case QEvent::PaletteChange: |
941 | mBackground = palette().color(QPalette::Base); |
942 | mForeground = palette().color(QPalette::Text); |
943 | mSelectionBackground = palette().color(QPalette::Highlight); |
944 | mSelectionForeground = palette().color(QPalette::HighlightedText); |
945 | mInactiveSelectionBackground = palette().color(QPalette::Inactive,QPalette::Highlight); |
946 | mInactiveSelectionForeground = palette().color(QPalette::Inactive,QPalette::HighlightedText); |
947 | break; |
948 | default: |
949 | break; |
950 | } |
951 | return QAbstractScrollArea::event(event); |
952 | } |
953 | |
954 | void QConsole::resizeEvent(QResizeEvent *) |
955 | { |
956 | //resize the cache image |
957 | std::shared_ptr<QImage> image = std::make_shared<QImage>(clientWidth(),clientHeight(), |
958 | QImage::Format_ARGB32); |
959 | if (mContentImage) { |
960 | //QRect newRect = image->rect().intersected(mContentImage->rect()); |
961 | QPainter painter(image.get()); |
962 | painter.fillRect(viewport()->rect(),mBackground); |
963 | // painter.drawImage(newRect,*mContentImage); |
964 | } |
965 | |
966 | mContentImage = image; |
967 | |
968 | sizeOrFontChanged(false); |
969 | } |
970 | |
971 | void QConsole::timerEvent(QTimerEvent *event) |
972 | { |
973 | if (event->timerId() == mBlinkTimerId) { |
974 | mBlinkStatus = 1- mBlinkStatus; |
975 | updateCaret(); |
976 | } |
977 | } |
978 | |
979 | void QConsole::inputMethodEvent(QInputMethodEvent *event) |
980 | { |
981 | if (mReadonly) |
982 | return; |
983 | QString s=event->commitString(); |
984 | if (!s.isEmpty()) |
985 | textInputed(s); |
986 | } |
987 | |
988 | void QConsole::wheelEvent(QWheelEvent *event) |
989 | { |
990 | if (event->angleDelta().y()>0) { |
991 | verticalScrollBar()->setValue(verticalScrollBar()->value()-1); |
992 | event->accept(); |
993 | return; |
994 | } else if (event->angleDelta().y()<0) { |
995 | verticalScrollBar()->setValue(verticalScrollBar()->value()+1); |
996 | event->accept(); |
997 | return; |
998 | } |
999 | } |
1000 | |
1001 | int ConsoleLines::rows() const |
1002 | { |
1003 | return mRows; |
1004 | } |
1005 | |
1006 | int ConsoleLines::lines() const |
1007 | { |
1008 | return mLines.count(); |
1009 | } |
1010 | |
1011 | void ConsoleLines::layout() |
1012 | { |
1013 | if (!mConsole) |
1014 | return; |
1015 | if (mLayouting) { |
1016 | mNeedRelayout = true; |
1017 | return; |
1018 | } |
1019 | mLayouting = true; |
1020 | mNeedRelayout = false; |
1021 | emit layoutStarted(); |
1022 | mRows = 0; |
1023 | bool forceUpdate = (mOldTabSize!=mConsole->tabSize()); |
1024 | for (PConsoleLine consoleLine: mLines) { |
1025 | if (forceUpdate || consoleLine->maxColumns > mConsole->columnsPerRow()) { |
1026 | consoleLine->maxColumns = breakLine(consoleLine->text,consoleLine->fragments); |
1027 | } |
1028 | mRows+=consoleLine->fragments.count(); |
1029 | } |
1030 | emit layoutFinished(); |
1031 | mLayouting = false; |
1032 | if (mNeedRelayout) |
1033 | emit needRelayout(); |
1034 | } |
1035 | |
1036 | ConsoleLines::ConsoleLines(QConsole *console) |
1037 | { |
1038 | mConsole = console; |
1039 | mRows = 0; |
1040 | mLayouting = false; |
1041 | mNeedRelayout = false; |
1042 | mOldTabSize = -1; |
1043 | mMaxLines = 1000; |
1044 | connect(this,&ConsoleLines::needRelayout,this,&ConsoleLines::layout); |
1045 | } |
1046 | |
1047 | void ConsoleLines::addLine(const QString &line) |
1048 | { |
1049 | PConsoleLine consoleLine=std::make_shared<ConsoleLine>(); |
1050 | consoleLine->text = line; |
1051 | consoleLine->maxColumns = breakLine(line,consoleLine->fragments); |
1052 | if (mLines.count()<mMaxLines || mMaxLines <= 0) { |
1053 | mLines.append(consoleLine); |
1054 | mRows += consoleLine->fragments.count(); |
1055 | emit rowsAdded(consoleLine->fragments.count()); |
1056 | } else { |
1057 | PConsoleLine firstLine = mLines[0]; |
1058 | mLines.pop_front(); |
1059 | mRows -= firstLine->fragments.count(); |
1060 | mLines.append(consoleLine); |
1061 | mRows += consoleLine->fragments.count(); |
1062 | emit layoutStarted(); |
1063 | emit layoutFinished(); |
1064 | } |
1065 | } |
1066 | |
1067 | void ConsoleLines::RemoveLastLine() |
1068 | { |
1069 | if (mLines.count()<=0) |
1070 | return; |
1071 | PConsoleLine consoleLine = mLines[mLines.count()-1]; |
1072 | mLines.pop_back(); |
1073 | mRows -= consoleLine->fragments.count(); |
1074 | emit lastRowsRemoved(consoleLine->fragments.count()); |
1075 | } |
1076 | |
1077 | void ConsoleLines::changeLastLine(const QString &newLine) |
1078 | { |
1079 | if (mLines.count()<=0) { |
1080 | return; |
1081 | } |
1082 | PConsoleLine consoleLine = mLines[mLines.count()-1]; |
1083 | int oldRows = consoleLine->fragments.count(); |
1084 | consoleLine->text = newLine; |
1085 | breakLine(newLine,consoleLine->fragments); |
1086 | int newRows = consoleLine->fragments.count(); |
1087 | if (newRows == oldRows) { |
1088 | emit lastRowsChanged(oldRows); |
1089 | return ; |
1090 | } else { |
1091 | mRows -= oldRows; |
1092 | mRows += newRows; |
1093 | emit layoutStarted(); |
1094 | emit layoutFinished(); |
1095 | } |
1096 | } |
1097 | |
1098 | QString ConsoleLines::getLastLine() |
1099 | { |
1100 | if (mLines.count()<=0) |
1101 | return "" ; |
1102 | return mLines[mLines.count()-1]->text; |
1103 | } |
1104 | |
1105 | QString ConsoleLines::getLine(int line) |
1106 | { |
1107 | if (line>=0 && line < mLines.count()) { |
1108 | return mLines[line]->text; |
1109 | } |
1110 | return "" ; |
1111 | } |
1112 | |
1113 | QChar ConsoleLines::getChar(int line, int ch) |
1114 | { |
1115 | QString s = getLine(line); |
1116 | if (ch>=0 && ch<s.length()) { |
1117 | return s[ch]; |
1118 | } else { |
1119 | return QChar(); |
1120 | } |
1121 | } |
1122 | |
1123 | QChar ConsoleLines::getChar(const LineChar &lineChar) |
1124 | { |
1125 | return getChar(lineChar.line,lineChar.ch); |
1126 | } |
1127 | |
1128 | QStringList ConsoleLines::getRows(int startRow, int endRow) |
1129 | { |
1130 | if (startRow>mRows) |
1131 | return QStringList(); |
1132 | if (startRow > endRow) |
1133 | return QStringList(); |
1134 | QStringList lst; |
1135 | int row = 0; |
1136 | for (PConsoleLine line:mLines) { |
1137 | for (const QString& s:line->fragments) { |
1138 | row+=1; |
1139 | if (row>endRow) { |
1140 | return lst; |
1141 | } |
1142 | if (row>=startRow) { |
1143 | lst.append(s); |
1144 | } |
1145 | } |
1146 | } |
1147 | return lst; |
1148 | } |
1149 | |
1150 | LineChar ConsoleLines::rowColumnToLineChar(const RowColumn &rowColumn) |
1151 | { |
1152 | return rowColumnToLineChar(rowColumn.row,rowColumn.column); |
1153 | } |
1154 | |
1155 | LineChar ConsoleLines::rowColumnToLineChar(int row, int column) |
1156 | { |
1157 | LineChar result{column,mLines.size()-1}; |
1158 | int rows=0; |
1159 | for (int i=0;i<mLines.size();i++) { |
1160 | PConsoleLine line = mLines[i]; |
1161 | if (row >= rows && row<rows+line->fragments.size()) { |
1162 | int r=row - rows; |
1163 | QString fragment = line->fragments[r]; |
1164 | int columnsBefore = 0; |
1165 | for (int j=0;j<fragment.size();j++) { |
1166 | QChar ch = fragment[j]; |
1167 | int charColumns= mConsole->charColumns(ch, columnsBefore); |
1168 | if (column>=columnsBefore && column<columnsBefore+charColumns) { |
1169 | result.ch = j; |
1170 | break; |
1171 | } |
1172 | } |
1173 | result.line = i; |
1174 | break; |
1175 | } |
1176 | rows += line->fragments.size(); |
1177 | } |
1178 | return result; |
1179 | } |
1180 | |
1181 | RowColumn ConsoleLines::lineCharToRowColumn(const LineChar &lineChar) |
1182 | { |
1183 | return lineCharToRowColumn(lineChar.line,lineChar.ch); |
1184 | } |
1185 | |
1186 | RowColumn ConsoleLines::lineCharToRowColumn(int line, int ch) |
1187 | { |
1188 | RowColumn result{ch,std::max(0,mRows-1)}; |
1189 | int rowsBefore = 0; |
1190 | if (line>=0 && line < mLines.size()) { |
1191 | for (int i=0;i<line;i++) { |
1192 | int rows = mLines[i]->fragments.size(); |
1193 | rowsBefore += rows; |
1194 | } |
1195 | PConsoleLine consoleLine = mLines[line]; |
1196 | int charsBefore = 0; |
1197 | for (int r=0;r<consoleLine->fragments.size();r++) { |
1198 | int chars = consoleLine->fragments[r].size(); |
1199 | if (r==consoleLine->fragments.size()-1 || (ch>=charsBefore && ch<charsBefore+chars)) { |
1200 | QString fragment = consoleLine->fragments[r]; |
1201 | int columnsBefore = 0; |
1202 | int len = std::min(ch-charsBefore,fragment.size()); |
1203 | for (int j=0;j<len;j++) { |
1204 | QChar ch = fragment[j]; |
1205 | int charColumns = mConsole->charColumns(ch,columnsBefore); |
1206 | columnsBefore += charColumns; |
1207 | } |
1208 | result.column=columnsBefore; |
1209 | result.row = rowsBefore + r; |
1210 | break; |
1211 | } |
1212 | charsBefore += chars; |
1213 | } |
1214 | } |
1215 | return result; |
1216 | } |
1217 | |
1218 | bool ConsoleLines::layouting() const |
1219 | { |
1220 | return mLayouting; |
1221 | } |
1222 | |
1223 | int ConsoleLines::breakLine(const QString &line, QStringList &fragments) |
1224 | { |
1225 | fragments.clear(); |
1226 | QString s = "" ; |
1227 | int maxColLen = 0; |
1228 | int columnsBefore = 0; |
1229 | for (QChar ch:line) { |
1230 | int charColumn = mConsole->charColumns(ch,columnsBefore); |
1231 | if (charColumn + columnsBefore > mConsole->columnsPerRow()) { |
1232 | if (ch == '\t') { |
1233 | if (columnsBefore != mConsole->columnsPerRow()) { |
1234 | charColumn = 0; |
1235 | } else |
1236 | charColumn = mConsole->tabSize(); |
1237 | } |
1238 | fragments.append(s); |
1239 | if (columnsBefore > maxColLen) { |
1240 | maxColLen = columnsBefore; |
1241 | } |
1242 | s = "" ; |
1243 | columnsBefore = 0; |
1244 | } |
1245 | if (charColumn > 0) { |
1246 | columnsBefore += charColumn; |
1247 | s += ch; |
1248 | } |
1249 | } |
1250 | if (fragments.count() == 0 || !s.isEmpty()) { |
1251 | fragments.append(s); |
1252 | if (columnsBefore > maxColLen) { |
1253 | maxColLen = columnsBefore; |
1254 | } |
1255 | } |
1256 | return maxColLen; |
1257 | } |
1258 | |
1259 | int ConsoleLines::getMaxLines() const |
1260 | { |
1261 | return mMaxLines; |
1262 | } |
1263 | |
1264 | int ConsoleLines::maxLines() const |
1265 | { |
1266 | return mMaxLines; |
1267 | } |
1268 | |
1269 | void ConsoleLines::setMaxLines(int maxLines) |
1270 | { |
1271 | mMaxLines = maxLines; |
1272 | if (mMaxLines > 0) { |
1273 | while (mLines.count()>mMaxLines) { |
1274 | mLines.pop_front(); |
1275 | } |
1276 | } |
1277 | } |
1278 | |
1279 | void ConsoleLines::clear() |
1280 | { |
1281 | mLines.clear(); |
1282 | mRows = 0; |
1283 | } |
1284 | |