1 /****************************************************************************
2 **
3 ** Copyright (C) 2006-2009 fullmetalcoder <fullmetalcoder@hotmail.fr>
4 **
5 ** This file is part of the Edyuk project <http://edyuk.org>
6 **
7 ** This file may be used under the terms of the GNU General Public License
8 ** version 3 as published by the Free Software Foundation and appearing in the
9 ** file GPL.txt included in the packaging of this file.
10 **
11 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
12 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
13 **
14 ****************************************************************************/
15
16 #include "qfoldpanel.h"
17
18 /*!
19 \file qfoldpanel.cpp
20 \brief Implementation of the QFoldPanel class.
21
22 \see QFoldPanel
23 */
24
25 #include "qeditor.h"
26
27 #include "qdocument.h"
28 #include "qdocumentline.h"
29
30 #include "qlanguagedefinition.h"
31
32 #include <QStack>
33 #include <QBitmap>
34 #include <QPainter>
35 #include <QScrollBar>
36 #include <QMouseEvent>
37
38 /*!
39 \ingroup widgets
40 @{
41 */
42
43 /*!
44 \class QFoldPanel
45 \brief A panel that draw fold indicators and provide fold/unfold actions to the user
46 */
47
QCE_AUTO_REGISTER(QFoldPanel)48 QCE_AUTO_REGISTER(QFoldPanel)
49
50 /*!
51 \brief Constructor
52 */
53 QFoldPanel::QFoldPanel(QWidget *p)
54 : QPanel(p)
55 {
56 setFixedWidth(12);
57 }
58
59 /*!
60 \brief Empty destructor
61 */
~QFoldPanel()62 QFoldPanel::~QFoldPanel()
63 {
64
65 }
66
67 /*!
68
69 */
type() const70 QString QFoldPanel::type() const
71 {
72 return "Fold indicators";
73 }
74
75 /*!
76
77 */
mousePressEvent(QMouseEvent * e)78 void QFoldPanel::mousePressEvent(QMouseEvent *e)
79 {
80 if ( !editor() || !editor()->languageDefinition() || (e->button() != Qt::LeftButton) )
81 {
82 QPanel::mousePressEvent(e);
83 return;
84 }
85
86 bool act = false;
87 QDocument *doc = editor()->document();
88 QLanguageDefinition *def = editor()->languageDefinition();
89
90 for ( int i = 0; i < m_rects.count(); ++i )
91 {
92 if ( !m_rects.at(i).contains(e->pos()) )
93 continue;
94
95 int ln = m_lines.at(i);
96
97 QDocumentLine b = doc->line(ln);
98
99 if ( b.hasFlag(QDocumentLine::CollapsedBlockStart) )
100 def->expand(doc, ln);
101 else if ( def->blockFlags(doc, ln, 0) & QLanguageDefinition::Collapsible )
102 def->collapse(doc, ln);
103
104 act = true;
105 }
106
107 if ( act )
108 editor()->setFocus();
109 else
110 QPanel::mousePressEvent(e);
111
112 }
113
114 /*!
115
116 */
paint(QPainter * p,QEditor * e)117 bool QFoldPanel::paint(QPainter *p, QEditor *e)
118 {
119 QDocument *doc = editor()->document();
120 QLanguageDefinition *def = e->languageDefinition();
121
122 if ( !def || !doc )
123 {
124 return true;
125 }
126
127 m_rects.clear();
128 m_lines.clear();
129
130 bool bVisible = false; //,
131 // inCursorBlock = false;
132
133 QDocumentLine block;
134 const QFontMetrics fm(doc->font());
135
136 int n,
137 pos,
138 depth = 0,
139 max = doc->lines(),
140 ls = fm.lineSpacing(),
141 pageBottom = e->viewport()->height(),
142 contentsY = e->verticalOffset();
143
144 pos = - contentsY;
145
146 //qDebug("beg pos : %i", pos);
147
148 for ( n = 0; n < max; ++n )
149 {
150 if ( pos > pageBottom )
151 break;
152
153 block = doc->line(n);
154
155 if ( block.isHidden() )
156 {
157 continue;
158 }
159
160 int len = ls * block.lineSpan();
161 int flags = def->blockFlags(doc, n, depth);
162 short open = QCE_FOLD_OPEN_COUNT(flags);
163 short close = QCE_FOLD_CLOSE_COUNT(flags);
164
165 bVisible = ((pos + len) >= 0);
166
167 int oldDepth = depth;
168
169 depth -= close;
170
171 if ( depth < 0 )
172 depth = 0;
173
174 depth += open;
175
176 if ( open )
177 {
178 if ( flags & QLanguageDefinition::Collapsed )
179 {
180 int bound = (ls - 8) / 2;
181 int mid = pos + len - ls / 6;
182
183 // outermost block folded : none of the opening is actually opened
184 depth -= open;
185
186 if ( bVisible )
187 {
188 // draw icon
189
190 if ( bound > 0 && oldDepth > 0 )
191 {
192 p->drawLine(7, pos, 7, pos + bound);
193 }
194
195 if ( close )
196 {
197 p->drawLine(7, pos + 8 + bound, 7, mid);
198 p->drawLine(7, mid, 12, mid);
199 }
200
201 m_lines << n;
202 m_rects << drawIcon(p, e, 3, pos + bound, true);
203 }
204
205 int sub = open;
206
207 //qDebug("%i : +%i", n, open);
208
209 while ( sub > 0 && ((n + 1) < max) )
210 {
211 ++n;
212 block = doc->line(n);
213
214 if ( !block.isHidden() )
215 {
216 if ( bVisible )
217 p->drawLine(7, pos + 8 + bound, 7, pos + len);
218
219 --n;
220 break;
221 }
222
223 int sflags = def->blockFlags(doc, n, depth + 1);
224 short sopen = QCE_FOLD_OPEN_COUNT(sflags);
225 short sclose = QCE_FOLD_CLOSE_COUNT(sflags);
226
227 sub -= sclose;
228
229 if ( sub <= 0 )
230 break;
231
232 sub += sopen;
233 }
234
235 depth += sub;
236
237 if ( bVisible && depth > 0 )
238 {
239 if ( close )
240 p->drawLine(7, mid, 7, pos + len);
241 else
242 p->drawLine(7, pos + 8 + bound, 7, pos + len);
243 }
244 } else {
245 if ( bVisible )
246 {
247 int bound = (ls - 8) / 2;
248
249 if ( oldDepth > 0 && bound > 0 )
250 p->drawLine(7, pos, 7, pos + bound);
251
252 m_lines << n;
253 m_rects << drawIcon(p, e, 3, pos + bound, false);
254
255 int mid = pos + len - ls / 6;
256
257 if ( close )
258 p->drawLine(7, mid, 12, mid);
259
260 if ( bound > 0 )
261 p->drawLine(7, pos + 8 + bound, 7, pos + len);
262 }
263 }
264 } else if ( (oldDepth > 0) && bVisible ) {
265 if ( close )
266 {
267 int mid = pos + len - ls / 6;
268
269 p->drawLine(7, pos, 7, mid);
270 p->drawLine(7, mid, 12, mid);
271
272 if ( depth > 0 )
273 p->drawLine(7, pos, 7, pos + len);
274 } else {
275 p->drawLine(7, pos, 7, pos + len);
276 }
277 }
278
279 pos += len;
280 }
281
282 return true;
283 }
284
drawIcon(QPainter * p,QEditor *,int x,int y,bool toExpand)285 QRect QFoldPanel::drawIcon( QPainter *p, QEditor *,
286 int x, int y, bool toExpand)
287 {
288 QRect symbolRect(x, y, 8, 8);
289
290 //p->save();
291 //p->setRenderHint(QPainter::Antialiasing);
292 p->drawRect(symbolRect);
293 //p->restore();
294
295 if ( toExpand )
296 {
297 p->drawLine(x + 2, y + 4, x + 6, y + 4);
298 p->drawLine(x + 4, y + 2, x + 4, y + 6);
299 } else {
300 p->drawLine(x + 2, y + 4, x + 6, y + 4);
301 }
302
303 return symbolRect;
304 }
305
306 /*! @} */
307