Maintain and highlight the current line.
[kdbg.git] / kdbg / textvw.cpp
blobe2310df3d23b21e57310777df013ea991c55e794
1 // $Id$
3 // Copyright by Johannes Sixt
4 // This file is under GPL, the GNU General Public Licence
6 #include <qpainter.h>
7 #include <qkeycode.h>
8 #include "textvw.h"
9 #include "textvw.moc"
10 #ifdef HAVE_CONFIG_H
11 #include "config.h"
12 #endif
13 #include "mydebug.h"
14 #include <iterator>
16 #define DEFAULT_WIDTH 100
17 #define DEFAULT_LINEHEIGHT 1
19 KTextView::KTextView(QWidget* parent, const char* name, WFlags f) :
20 TableView(parent, name, f),
21 m_width(DEFAULT_WIDTH),
22 m_height(DEFAULT_LINEHEIGHT),
23 m_tabWidth(0),
24 m_curRow(-1)
26 setNumCols(1);
27 setBackgroundColor(colorGroup().base());
30 KTextView::~KTextView()
35 * Update cell width and hight; returns whether there is a change
36 * Cell geometry: There are 2 pixels to the left and to the right
37 * and 1 pixel _below_ the line
39 bool KTextView::updateCellSize(const QString& text)
41 QPainter p(this);
42 setupPainter(&p);
43 QRect r = p.boundingRect(0,0, 0,0,
44 AlignLeft | SingleLine | DontClip | ExpandTabs,
45 text, text.length());
47 bool update = false;
48 int w = r.width() + 4;
49 if (w > m_width) {
50 m_width = w;
51 update = true;
53 int h = r.height() + 1;
54 if (h > m_height) {
55 m_height = h;
56 update = true;
58 return update;
61 void KTextView::setText(const QString& text)
63 QStringList l = QStringList::split('\n', text, true);
65 bool autoU = autoUpdate();
66 setAutoUpdate(false);
68 int lineNo = QMIN(m_texts.size(), l.size());
69 for (int i = 0; i < lineNo; i++) {
70 replaceLine(i, l[i]);
72 if (l.size() > m_texts.size()) {
73 // the new text has more lines than the old one
74 // here lineNo is the number of lines of the old text
75 for (size_t i = lineNo; i < l.size(); i++) {
76 insertLine(l[i]);
78 } else {
79 // the new file has fewer lines
80 // here lineNo is the number of lines of the new file
81 // remove the excessive lines
82 m_texts.resize(lineNo);
83 setNumRows(lineNo);
86 setAutoUpdate(autoU);
87 if (autoU) {
88 updateTableSize();
89 update();
92 // if the cursor is in the deleted lines, move it to the last line
93 if (m_curRow >= int(m_texts.size())) {
94 m_curRow = -1; /* at this point don't have an active row */
95 activateLine(m_texts.size()-1); /* now we have */
99 void KTextView::insertLine(const QString& text)
101 m_texts.push_back(text);
102 setNumRows(m_texts.size());
104 updateCellSize(text);
106 if (autoUpdate()) {
107 updateTableSize();
108 repaint();
113 * TODO: This function doesn't shrink the line length if the longest line
114 * is replaced by a shorter one.
116 void KTextView::replaceLine(int line, const QString& text)
118 if (line < 0 || line >= int(m_texts.size()))
119 return;
121 m_texts[line] = text;
123 bool update = updateCellSize(text);
125 if (autoUpdate()) {
126 if (update) {
127 updateTableSize();
129 repaint();
133 void KTextView::insertParagraph(const QString& text, int row)
135 m_texts.insert(m_texts.begin()+row, text);
137 // update line widths
138 updateCellSize(text);
140 setNumRows(m_texts.size());
142 if (autoUpdate() && isVisible()) {
143 updateTableSize();
144 update();
148 void KTextView::removeParagraph(int row)
150 m_texts.erase(m_texts.begin()+row);
151 setNumRows(m_texts.size());
153 if (autoUpdate() && isVisible()) {
154 updateTableSize();
155 update();
159 void KTextView::setCursorPosition(int row, int)
161 activateLine(row);
164 void KTextView::cursorPosition(int* row, int* col)
166 *row = m_curRow;
167 *col = 0;
170 int KTextView::cellWidth(int /*col*/) const
172 return m_width;
175 int KTextView::cellHeight(int /*row*/) const
177 return m_height;
180 int KTextView::textCol() const
182 // by default, the text is in column 0
183 return 0;
186 void KTextView::paintCell(QPainter* p, int row, int /*col*/)
188 if (row >= int(m_texts.size())) {
189 return;
191 if (row == m_curRow) {
192 // paint background
193 p->fillRect(0,0, m_width,m_height, QBrush(colorGroup().background()));
195 const QString& text = m_texts[row];
196 p->drawText(2,0, m_width-4, m_height,
197 AlignLeft | SingleLine | DontClip | ExpandTabs,
198 text, text.length());
201 void KTextView::keyPressEvent(QKeyEvent* ev)
203 int oldRow = m_curRow;
205 // go to line 0 if no current line
206 if (m_curRow < 0) {
207 activateLine(0);
209 else
211 int numVisibleRows, newRow, newX;
213 switch (ev->key()) {
214 case Key_Up:
215 if (m_curRow > 0) {
216 activateLine(m_curRow-1);
218 break;
219 case Key_Down:
220 if (m_curRow < numRows()-1) {
221 activateLine(m_curRow+1);
223 break;
224 case Key_PageUp:
225 /* this method doesn't work for variable height lines */
226 numVisibleRows = lastRowVisible()-topCell();
227 newRow = m_curRow - QMAX(numVisibleRows,1);
228 if (newRow < 0)
229 newRow = 0;
230 activateLine(newRow);
231 break;
232 case Key_PageDown:
233 /* this method doesn't work for variable height lines */
234 numVisibleRows = lastRowVisible()-topCell();
235 newRow = m_curRow + QMAX(numVisibleRows,1);
236 if (newRow >= numRows())
237 newRow = numRows()-1;
238 activateLine(newRow);
239 break;
241 // scroll left and right by a few pixels
242 case Key_Left:
243 newX = xOffset() - viewWidth()/10;
244 setXOffset(QMAX(newX, 0));
245 break;
246 case Key_Right:
247 newX = xOffset() + viewWidth()/10;
248 setXOffset(QMIN(newX, maxXOffset()));
249 break;
251 default:
252 QWidget::keyPressEvent(ev);
253 return;
256 // make row visible
257 if (m_curRow != oldRow && !rowIsVisible(m_curRow)) {
258 // if the old row was visible, we scroll the view by some pixels
259 // if the old row was not visible, we place active row at the top
260 if (oldRow >= 0 && rowIsVisible(oldRow)) {
261 int diff = m_curRow - oldRow;
262 int newTop = topCell() + diff;
263 setTopCell(QMAX(newTop,0));
264 } else {
265 setTopCell(m_curRow);
268 ev->accept();
271 void KTextView::mousePressEvent(QMouseEvent* ev)
273 int row = findRow(ev->y());
274 activateLine(row);
277 void KTextView::focusInEvent(QFocusEvent*)
280 * The base class does a repaint(), which causes flicker. So we do
281 * nothing here.
285 void KTextView::focusOutEvent(QFocusEvent*)
288 * The base class does a repaint(), which causes flicker. So we do
289 * nothing here.
293 void KTextView::activateLine(int row)
295 if (row == m_curRow)
296 return;
298 int col = textCol();
300 int oldRow = m_curRow;
301 // note that row may be < 0
302 m_curRow = row;
303 // must change m_curRow first, so that updateCell(oldRow) erases the old line!
304 if (oldRow >= 0) {
305 updateCell(oldRow, col);
307 if (row >= 0) {
308 updateCell(row, col);
310 emit lineChanged();
313 /* This is needed to make the kcontrol's color setup working */
314 void KTextView::paletteChange(const QPalette& oldPal)
316 setBackgroundColor(colorGroup().base());
317 TableView::paletteChange(oldPal);
319 // recompute window size
320 m_width = DEFAULT_WIDTH;
321 m_height = DEFAULT_LINEHEIGHT;
322 for (size_t i = 0; i < m_texts.size(); i++) {
323 updateCellSize(m_texts[i]);
325 updateTableSize();
328 void KTextView::setTabWidth(int numChars)
330 QFontMetrics fm = font();
331 int newTabWidth = fm.width('x') * numChars;
332 if (newTabWidth == m_tabWidth)
333 return;
334 m_tabWidth = newTabWidth;
336 // recompute window width
337 m_width = DEFAULT_WIDTH;
338 for (size_t i = 0; i < m_texts.size(); i++) {
339 updateCellSize(m_texts[i]);
342 updateTableSize();
343 if (autoUpdate()) {
344 repaint();
348 void KTextView::setupPainter(QPainter* p)
350 p->setTabStops(m_tabWidth);
353 int KTextView::charAt(const QPoint& p, int* para)
355 if (findCol(p.x()) != textCol())
356 return *para = -1;
357 int row = findRow(p.y());
358 *para = row;
359 if (row < 0)
360 return -1;
362 // find top-left corner of text cell
363 int top, left;
364 if (!colXPos(textCol(), &left))
365 return -1;
366 if (!rowYPos(row, &top))
367 return -1;
369 // get the bounding rect of the text
370 QPainter painter(this);
371 setupPainter(&painter);
372 const QString& text = m_texts[row];
373 QRect bound =
374 painter.boundingRect(left+2, top, 0,0,
375 AlignLeft | SingleLine | DontClip | ExpandTabs,
376 text, text.length());
377 if (!bound.contains(p))
378 return -1; /* p is outside text */
380 for (uint n = 0; n < text.length(); n++)
383 * If p is in the rectangle that has n+1 characters, than n
384 * is the character we are looking for
386 bound =
387 painter.boundingRect(left+2, top, 0,0,
388 AlignLeft | SingleLine | DontClip | ExpandTabs,
389 text, n+1);
390 if (bound.contains(p))
391 return n;
393 return -1;