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