Developer Documentation
Loading...
Searching...
No Matches
TextBrowserWidget.cc
1
2/*===========================================================================*\
3* *
4* OpenFlipper *
5 * Copyright (c) 2001-2015, RWTH-Aachen University *
6 * Department of Computer Graphics and Multimedia *
7 * All rights reserved. *
8 * www.openflipper.org *
9 * *
10 *---------------------------------------------------------------------------*
11 * This file is part of OpenFlipper. *
12 *---------------------------------------------------------------------------*
13 * *
14 * Redistribution and use in source and binary forms, with or without *
15 * modification, are permitted provided that the following conditions *
16 * are met: *
17 * *
18 * 1. Redistributions of source code must retain the above copyright notice, *
19 * this list of conditions and the following disclaimer. *
20 * *
21 * 2. Redistributions in binary form must reproduce the above copyright *
22 * notice, this list of conditions and the following disclaimer in the *
23 * documentation and/or other materials provided with the distribution. *
24 * *
25 * 3. Neither the name of the copyright holder nor the names of its *
26 * contributors may be used to endorse or promote products derived from *
27 * this software without specific prior written permission. *
28 * *
29 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
30 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED *
31 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
32 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER *
33 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, *
34 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, *
35 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR *
36 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
37 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING *
38 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS *
39 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
40* *
41\*===========================================================================*/
42
43#include <QtWidgets>
44
45
46#include "TextBrowserWidget.hh"
47
48QString const TextBrowserWidget::startRenderObjectTag_ = "name:";
49QString const TextBrowserWidget::startVertexShaderTag_ = "--vertex-shader--";
50QString const TextBrowserWidget::endVertexShaderTag_ = "--end-vertex-shader--";
51QString const TextBrowserWidget::startTessControlShaderTag_ = "---tesscontrol-shader--";
52QString const TextBrowserWidget::endTessControlShaderTag_ = "--end-tesscontrol-shader--";
53QString const TextBrowserWidget::startTessEvalShaderTag_ = "--tesseval-shader--";
54QString const TextBrowserWidget::endTessEvalShaderTag_ = "--end-tesseval-shader--";
55QString const TextBrowserWidget::startGeometryShaderTag_ = "--geometry-shader--";
56QString const TextBrowserWidget::endGeometryShaderTag_ = "--end-geometry-shader--";
57QString const TextBrowserWidget::startFragmentShaderTag_ = "--fragment-shader--";
58QString const TextBrowserWidget::endFragmentShaderTag_ = "--end-fragment-shader--";
59
60
61TextBrowserWidget::TextBrowserWidget(QWidget *parent) : QPlainTextEdit(parent) {
62 sideArea_ = new TextBrowserSideArea(this);
63 updateTextBrowserSideAreaWidth();
64
65 connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateTextBrowserSideAreaWidth()));
66 connect(this, SIGNAL(blockCountChanged(int)), this, SLOT(updateFolds()));
67 connect(this, SIGNAL(updateRequest(QRect,int)), this, SLOT(updateTextBrowserSideArea(QRect,int)));
68
69 setReadOnly(true);
70}
71
72
73
74int TextBrowserWidget::sideAreaWidth() {
75 int digits = 1;
76 int max = qMax(1, blockCount());
77 while (max >= 10) {
78 max /= 10;
79 ++digits;
80 }
81
82 int space = 3 + fontMetrics().horizontalAdvance(QLatin1Char('9')) * digits;
83
84 return space;
85}
86
87
88
89void TextBrowserWidget::updateTextBrowserSideAreaWidth() {
90 setViewportMargins(sideAreaWidth(), 0, 0, 0);
91}
92
93void TextBrowserWidget::updateTextBrowserSideArea(const QRect &rect, int dy) {
94 if (dy)
95 sideArea_->scroll(0, dy);
96 else
97 sideArea_->update(0, rect.y(), sideArea_->width(), rect.height());
98
99 if (rect.contains(viewport()->rect()))
100 updateTextBrowserSideAreaWidth();
101}
102
103
104
105void TextBrowserWidget::resizeEvent(QResizeEvent *e) {
106 QPlainTextEdit::resizeEvent(e);
107
108 QRect cr = contentsRect();
109 sideArea_->setGeometry(QRect(cr.left(), cr.top(), sideAreaWidth(), cr.height()));
110}
111
112void TextBrowserWidget::sideAreaPaintEvent(QPaintEvent *event) {
113
114 QPainter painter(sideArea_);
115 painter.fillRect(event->rect(), Qt::lightGray);
116 painter.setPen(Qt::black);
117
118 QTextBlock block = firstVisibleBlock();
119
120 int blockNumber = block.blockNumber();
121 int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
122 int bottom = top + (int) blockBoundingRect(block).height();
123
124 Fold found_fold;
125 while (block.isValid() && top <= event->rect().bottom()) {
126 if (block.isVisible() && bottom >= event->rect().top()) {
127 if (getFold(block.position(), found_fold)) {
128 if (found_fold.type == SHADER) {
129 int fold_first_block = document()->findBlock(found_fold.start).blockNumber();
130 QString text = block.text();
131 // only draw line numbers on actual shader code
132 if (text.contains(TextBrowserWidget::startVertexShaderTag_) ||
133 text.contains(TextBrowserWidget::endVertexShaderTag_) ||
134 text.contains(TextBrowserWidget::startTessControlShaderTag_) ||
135 text.contains(TextBrowserWidget::endTessControlShaderTag_) ||
136 text.contains(TextBrowserWidget::startTessEvalShaderTag_) ||
137 text.contains(TextBrowserWidget::endTessEvalShaderTag_) ||
138 text.contains(TextBrowserWidget::startGeometryShaderTag_) ||
139 text.contains(TextBrowserWidget::endGeometryShaderTag_) ||
140 text.contains(TextBrowserWidget::startFragmentShaderTag_) ||
141 text.contains(TextBrowserWidget::endFragmentShaderTag_)) {
142 if (found_fold.folded)
143 painter.drawText(0, top, sideArea_->width(), fontMetrics().height(),Qt::AlignRight, "+");
144 else
145 painter.drawText(0, top, sideArea_->width(), fontMetrics().height(),Qt::AlignRight, "-");
146 } else {
147 QString number = QString::number(blockNumber - fold_first_block);
148 painter.drawText(0, top, sideArea_->width(), fontMetrics().height(),Qt::AlignRight, number);
149 }
150 } else {
151 if (found_fold.folded)
152 painter.drawText(0, top, sideArea_->width(), fontMetrics().height(),Qt::AlignRight, "+");
153 else
154 painter.drawText(0, top, sideArea_->width(), fontMetrics().height(),Qt::AlignRight, "-");
155 }
156 } else
157 painter.drawText(0, top, sideArea_->width(), fontMetrics().height(),Qt::AlignRight, " ");
158 }
159
160 block = block.next();
161 top = bottom;
162 bottom = top + (int) blockBoundingRect(block).height();
163 ++blockNumber;
164 }
165}
166
167bool TextBrowserWidget::getFold(int _position, Fold& _fold) {
168 std::map<int,size_t>::iterator it = blockPosToFold_.find(_position);
169 if (!folds_.empty() && it != blockPosToFold_.end()) {
170 _fold = folds_[it->second];
171 return true;
172 } else
173 return false;
174}
175
176void TextBrowserWidget::mouseDoubleClickEvent(QMouseEvent* e) {
177 QTextBlock block = firstVisibleBlock();
178 int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top();
179 int bottom = top + (int) blockBoundingRect(block).height();
180 const int y = e->pos().y();
181 // find the block that was clicked and toggle the folding
182 while (block.isValid()) {
183 if (top <= y && y <= bottom) {
184 toggleFold(block.position());
185 break;
186 }
187
188 block = block.next();
189 top = bottom;
190 bottom = top + (int) blockBoundingRect(block).height();
191 }
192}
193
194void TextBrowserWidget::foldAll() {
195 for (std::vector<Fold>::iterator it = folds_.begin(); it != folds_.end(); ++it) {
196 fold(*it);
197 }
198}
199
200void TextBrowserWidget::unfoldAll() {
201 for (std::vector<Fold>::iterator it = folds_.begin(); it != folds_.end(); ++it) {
202 unfold(*it);
203 }
204}
205
206void TextBrowserWidget::fold(Fold& _fold) {
207 if (_fold.folded)
208 return;
209
210 QTextBlock startBlock = document()->findBlock(_fold.start);
211 QTextBlock endBlock = document()->findBlock(_fold.end);
212
213 startBlock = startBlock.next();
214 while (startBlock.isValid() && startBlock != endBlock) {
215 startBlock.setVisible(false);
216 startBlock = startBlock.next();
217 }
218 if (_fold.type == RENDEROBJECT)
219 endBlock.setVisible(false);
220
221 _fold.folded = true;
222 document()->markContentsDirty(_fold.start, _fold.end - _fold.start);
223}
224
225void TextBrowserWidget::unfold(Fold& _fold) {
226 if (!_fold.folded)
227 return;
228
229 QTextBlock startBlock = document()->findBlock(_fold.start);
230 QTextBlock endBlock = document()->findBlock(_fold.end);
231
232 startBlock = startBlock.next();
233 while (startBlock.isValid() && startBlock != endBlock) {
234 startBlock.setVisible(true);
235 startBlock = startBlock.next();
236 }
237 if (_fold.type == RENDEROBJECT)
238 endBlock.setVisible(true);
239
240 _fold.folded = false;
241 document()->markContentsDirty(_fold.start, _fold.end-_fold.start);
242}
243
244void TextBrowserWidget::toggleFold(int _position) {
245 for (std::vector<Fold>::iterator it = folds_.begin(); it != folds_.end(); ++it) {
246 if (it->contains(_position)) {
247 if (it->folded)
248 unfold(*it);
249 else
250 fold(*it);
251
252 break;
253 }
254 }
255}
256
257void TextBrowserWidget::updateFolds() {
258 folds_.clear();
259
260 // search for all vertex shader
261 QTextCursor startCursor = document()->find(TextBrowserWidget::startVertexShaderTag_, 0, QTextDocument::FindWholeWords);
262 QTextCursor endCursor = document()->find(TextBrowserWidget::endVertexShaderTag_, 0, QTextDocument::FindWholeWords);
263
264 while (!startCursor.isNull() && !endCursor.isNull()) {
265 startCursor.movePosition(QTextCursor::StartOfLine);
266 endCursor.movePosition(QTextCursor::EndOfLine);
267 folds_.push_back(Fold(startCursor.position(),endCursor.position(),SHADER));
268
269 // map block position to fold
270 int startPos = startCursor.position();
271 const int endPos = endCursor.position();
272 for (; startPos < endPos; ++startPos) {
273 QTextBlock block = document()->findBlock(startPos);
274 blockPosToFold_[block.position()] = folds_.size() - 1;
275 }
276
277 bool moved = startCursor.movePosition(QTextCursor::Down);
278 if (!moved)
279 break;
280 moved = endCursor.movePosition(QTextCursor::Down);
281 if (!moved)
282 break;
283
284 startCursor = document()->find(TextBrowserWidget::startVertexShaderTag_, startCursor, QTextDocument::FindWholeWords);
285 endCursor = document()->find(TextBrowserWidget::endVertexShaderTag_, endCursor, QTextDocument::FindWholeWords);
286 }
287
288 // search for all tesscontrol shader
289 startCursor = document()->find(TextBrowserWidget::startTessControlShaderTag_, 0, QTextDocument::FindWholeWords);
290 endCursor = document()->find(TextBrowserWidget::endTessControlShaderTag_, 0, QTextDocument::FindWholeWords);
291
292 while (!startCursor.isNull() && !endCursor.isNull()) {
293 startCursor.movePosition(QTextCursor::StartOfLine);
294 endCursor.movePosition(QTextCursor::EndOfLine);
295 folds_.push_back(Fold(startCursor.position(),endCursor.position(),SHADER));
296
297 // map block position to fold
298 int startPos = startCursor.position();
299 const int endPos = endCursor.position();
300 for (; startPos < endPos; ++startPos) {
301 QTextBlock block = document()->findBlock(startPos);
302 blockPosToFold_[block.position()] = folds_.size() - 1;
303 }
304
305 bool moved = startCursor.movePosition(QTextCursor::Down);
306 if (!moved)
307 break;
308 moved = endCursor.movePosition(QTextCursor::Down);
309 if (!moved)
310 break;
311
312 startCursor = document()->find(TextBrowserWidget::startTessControlShaderTag_, startCursor, QTextDocument::FindWholeWords);
313 endCursor = document()->find(TextBrowserWidget::endTessControlShaderTag_, endCursor, QTextDocument::FindWholeWords);
314 }
315
316 // search for all tesseval shader
317 startCursor = document()->find(TextBrowserWidget::startTessEvalShaderTag_, 0, QTextDocument::FindWholeWords);
318 endCursor = document()->find(TextBrowserWidget::endTessEvalShaderTag_, 0, QTextDocument::FindWholeWords);
319
320 while (!startCursor.isNull() && !endCursor.isNull()) {
321 startCursor.movePosition(QTextCursor::StartOfLine);
322 endCursor.movePosition(QTextCursor::EndOfLine);
323 folds_.push_back(Fold(startCursor.position(),endCursor.position(),SHADER));
324
325 // map block position to fold
326 int startPos = startCursor.position();
327 const int endPos = endCursor.position();
328 for (; startPos < endPos; ++startPos) {
329 QTextBlock block = document()->findBlock(startPos);
330 blockPosToFold_[block.position()] = folds_.size() - 1;
331 }
332
333 bool moved = startCursor.movePosition(QTextCursor::Down);
334 if (!moved)
335 break;
336 moved = endCursor.movePosition(QTextCursor::Down);
337 if (!moved)
338 break;
339
340 startCursor = document()->find(TextBrowserWidget::startTessEvalShaderTag_, startCursor, QTextDocument::FindWholeWords);
341 endCursor = document()->find(TextBrowserWidget::endTessEvalShaderTag_, endCursor, QTextDocument::FindWholeWords);
342 }
343
344 // search for all geometry shader
345 startCursor = document()->find(TextBrowserWidget::startGeometryShaderTag_, 0, QTextDocument::FindWholeWords);
346 endCursor = document()->find(TextBrowserWidget::endGeometryShaderTag_, 0, QTextDocument::FindWholeWords);
347
348 while (!startCursor.isNull() && !endCursor.isNull()) {
349 startCursor.movePosition(QTextCursor::StartOfLine);
350 endCursor.movePosition(QTextCursor::EndOfLine);
351 folds_.push_back(Fold(startCursor.position(),endCursor.position(),SHADER));
352
353 // map block position to fold
354 int startPos = startCursor.position();
355 const int endPos = endCursor.position();
356 for (; startPos < endPos; ++startPos) {
357 QTextBlock block = document()->findBlock(startPos);
358 blockPosToFold_[block.position()] = folds_.size() - 1;
359 }
360
361 bool moved = startCursor.movePosition(QTextCursor::Down);
362 if (!moved)
363 break;
364 moved = endCursor.movePosition(QTextCursor::Down);
365 if (!moved)
366 break;
367
368 startCursor = document()->find(TextBrowserWidget::startGeometryShaderTag_, startCursor, QTextDocument::FindWholeWords);
369 endCursor = document()->find(TextBrowserWidget::endGeometryShaderTag_, endCursor, QTextDocument::FindWholeWords);
370 }
371
372 // search for all fragment shader
373 startCursor = document()->find(TextBrowserWidget::startFragmentShaderTag_, 0, QTextDocument::FindWholeWords);
374 endCursor = document()->find(TextBrowserWidget::endFragmentShaderTag_, 0, QTextDocument::FindWholeWords);
375
376 while (!startCursor.isNull() && !endCursor.isNull()) {
377 startCursor.movePosition(QTextCursor::StartOfLine);
378 endCursor.movePosition(QTextCursor::EndOfLine);
379 folds_.push_back(Fold(startCursor.position(),endCursor.position(),SHADER));
380
381 // map block position to fold
382 int startPos = startCursor.position();
383 const int endPos = endCursor.position();
384 for (; startPos < endPos; ++startPos) {
385 QTextBlock block = document()->findBlock(startPos);
386 blockPosToFold_[block.position()] = folds_.size() - 1;
387 }
388
389 bool moved = startCursor.movePosition(QTextCursor::Down);
390 if (!moved)
391 break;
392 moved = endCursor.movePosition(QTextCursor::Down);
393 if (!moved)
394 break;
395
396 startCursor = document()->find(TextBrowserWidget::startFragmentShaderTag_, startCursor, QTextDocument::FindWholeWords);
397 endCursor = document()->find(TextBrowserWidget::endFragmentShaderTag_, endCursor, QTextDocument::FindWholeWords);
398 }
399
400 // search for all render objects
401 startCursor = document()->find(TextBrowserWidget::startRenderObjectTag_, 0, QTextDocument::FindWholeWords);
402 endCursor = document()->find(TextBrowserWidget::startVertexShaderTag_, 0, QTextDocument::FindWholeWords);
403
404 while (!startCursor.isNull() && !endCursor.isNull()) {
405 startCursor.movePosition(QTextCursor::StartOfLine);
406 // vertex shader does not belong to this fold
407 endCursor.movePosition(QTextCursor::Up);
408 endCursor.movePosition(QTextCursor::EndOfLine);
409 folds_.push_back(Fold(startCursor.position(),endCursor.position(),RENDEROBJECT));
410
411 // map block position to fold
412 int startPos = startCursor.position();
413 const int endPos = endCursor.position();
414 for (; startPos < endPos; ++startPos) {
415 QTextBlock block = document()->findBlock(startPos);
416 blockPosToFold_[block.position()] = folds_.size() - 1;
417 }
418
419 bool moved = startCursor.movePosition(QTextCursor::Down);
420 if (!moved)
421 break;
422 // skip to after the vertex shader starts
423 moved = endCursor.movePosition(QTextCursor::Down);
424 if (!moved)
425 break;
426 moved = endCursor.movePosition(QTextCursor::Down);
427 if (!moved)
428 break;
429
430 startCursor = document()->find(TextBrowserWidget::startRenderObjectTag_, startCursor, QTextDocument::FindWholeWords);
431 endCursor = document()->find(TextBrowserWidget::startVertexShaderTag_, endCursor, QTextDocument::FindWholeWords);
432 }
433
434 // fold shader blocks
435 foldAll();
436}
std::map< int, size_t > blockPosToFold_
maps positions in the document to indices in folds_
bool getFold(int _position, Fold &_fold)
get the _fold corresponding to the document _position