1 /*
2 	Copyright (C) 2010 Andres Cabrera
3 	mantaraya36@gmail.com
4 
5 	This file is part of CsoundQt.
6 
7 	CsoundQt is free software; you can redistribute it
8 	and/or modify it under the terms of the GNU Lesser General Public
9 	License as published by the Free Software Foundation; either
10 	version 2.1 of the License, or (at your option) any later version.
11 
12 	CsoundQt is distributed in the hope that it will be useful,
13 	but WITHOUT ANY WARRANTY; without even the implied warranty of
14 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 	GNU Lesser General Public License for more details.
16 
17 	You should have received a copy of the GNU Lesser General Public
18 	License along with Csound; if not, write to the Free Software
19 	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 	02111-1307 USA
21 */
22 
23 #ifdef USE_QT5
24 #include <QtWidgets>
25 #else
26 #include <QtGui>
27 #endif
28 
29 #include "texteditor.h"
30 
31 
TextEditor(QWidget * parent)32 TextEditor::TextEditor(QWidget *parent) :
33 	QTextEdit(parent)
34 {
35 	setAcceptDrops(true);
36 	setAcceptRichText(false);
37 	m_parameterMode = false;
38 	m_commaTyped = false;
39 	//  qDebug() << "TextEditor::TextEditor" << acceptDrops();
40 }
41 
keyPressEvent(QKeyEvent * event)42 void TextEditor::keyPressEvent (QKeyEvent * event)
43 {
44 	if (m_commaTyped && event->key() != Qt::Key_Space) {
45 		m_commaTyped = false;
46 	}
47 	switch(event->key()) {
48 	case Qt::Key_Tab:
49         if (m_parameterMode) {
50             emit tabPressed();
51         } else if(m_tabIndents) {
52             // this blocks default behaviour and only tab does not move any further
53 			emit requestIndent();
54         } else {
55             QTextEdit::keyPressEvent(event);
56 		}
57 		return;
58 	case Qt::Key_Backtab:
59 		if (m_parameterMode) {
60 			emit backtabPressed();
61 		} else if(m_tabIndents) {
62 			emit requestUnindent();
63 		}
64 		return;
65 	case Qt::Key_Return:
66 	case Qt::Key_Enter:
67 		emit enterPressed();
68 		break;
69 	case Qt::Key_Space:
70 		if (m_commaTyped) {
71 			emit showParameterInfo();
72 		}
73 		m_commaTyped = false;
74 		break;
75 	case Qt::Key_Comma:
76 		m_commaTyped = true;
77 		break;
78 	}
79 	QTextEdit::keyPressEvent(event); // Process key events in the rest of the application
80 	if (event->key() == Qt::Key_Up || event->key() == Qt::Key_Down
81 			|| event->key() == Qt::Key_Left || event->key() == Qt::Key_Right) {
82 		emit arrowPressed();
83 	}
84 
85 	if (event->key() == Qt::Key_Escape) {
86 		emit escapePressed();
87 	} else if(event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) {
88 		emit newLine();
89 	}
90     return;
91 }
92 
mouseReleaseEvent(QMouseEvent * e)93 void TextEditor::mouseReleaseEvent(QMouseEvent *e)
94 {
95 	QTextEdit::mouseReleaseEvent(e); // and do all the necessary qt job
96 	if (m_parameterMode) {
97 		emit requestParameterModeExit();
98 	}
99 
100 }
101 
102 //The following makes the editor almost accept drop events on OS X, but breaks all dragging on the same document on linux
103 //void TextEditor::dropEvent(QDropEvent *event)
104 //{
105 //  qDebug() << "TextEditor::dropEvent" << event->format();
106 ////  QString fileName = QString(event->encodedData("text/uri-list")).remove("file://");
107 ////  event->;
108 //
109 //  event->acceptProposedAction();
110 //}
111 //
112 //void TextEditor::dragEnterEvent(QDragEnterEvent *event)
113 //{
114 ////  qDebug() << "TextEditor::dragEnterEvent" << event->format();
115 //  // TODO remove file:// text from text
116 //  event->acceptProposedAction();
117 //}
118 //
119 //void TextEditor::dragMoveEvent(QDragMoveEvent *event)
120 //{
121 ////  qDebug() << "TextEditor::dragMoveEvent" << event->mimeData()->text();
122 //  event->acceptProposedAction();
123 //}
124 
TextEditLineNumbers(QWidget * parent)125 TextEditLineNumbers::TextEditLineNumbers(QWidget *parent)
126 	: TextEditor(parent)
127 {
128 	setLineAreaVisible(false);
129 	lineNumberArea = new LineNumberArea(this);
130 	connect(this->document(),SIGNAL(blockCountChanged(int)),this,SLOT(updateLineArea(int)));
131 	connect(this->verticalScrollBar(),SIGNAL(valueChanged(int)),this,SLOT(updateLineArea(int)));
132 	connect(this,SIGNAL(cursorPositionChanged()),this,SLOT(updateLineArea()));
133     lineNumberArea->setLineNumberSizeScaling(0.8);
134     m_editorPadding = 6;
135 }
136 
getAreaWidth()137 int TextEditLineNumbers::getAreaWidth()
138 {
139     qreal scaling = lineNumberArea->lineNumberSizeScaling();
140     int right_padding = lineNumberArea->padding();
141     int left_padding  = lineNumberArea->padding();
142     int numDigits = 4;
143     int oneDigit = this->fontMetrics().width(QLatin1Char('9'));
144     return oneDigit * scaling * numDigits + left_padding + right_padding;
145 }
146 
resizeEvent(QResizeEvent * e)147 void TextEditLineNumbers::resizeEvent(QResizeEvent *e)
148 {
149 	QTextEdit::resizeEvent(e);
150 	if (m_lineAreaVisble) {
151         setViewportMargins(getAreaWidth()+m_editorPadding, 0, 0, 0); // since font might have changed
152 		QRect cr = contentsRect();
153 		lineNumberArea->setGeometry(QRect(cr.left(), cr.top(), getAreaWidth(), cr.height()));
154     } else
155         setViewportMargins(m_editorPadding, 0, 0, 0); // since font might have changed
156 
157 }
158 
setLineAreaVisible(bool visible)159 void TextEditLineNumbers::setLineAreaVisible(bool visible)
160 {
161 	m_lineAreaVisble = visible;
162 	if (m_lineAreaVisble) {
163         setViewportMargins(getAreaWidth() + m_editorPadding, 0, 0, 0);
164 	}
165 	else {
166         setViewportMargins(m_editorPadding, 0, 0, 0);
167 	}
168 }
169 
markDebugLine(int line)170 void TextEditLineNumbers::markDebugLine(int line)
171 {
172 	m_debugLines.append(line);
173 	lineNumberArea->setDebugLines(m_debugLines);
174 }
175 
unmarkDebugLine(int line)176 void TextEditLineNumbers::unmarkDebugLine(int line)
177 {
178 	int index = m_debugLines.indexOf(line);
179 	if (index >= 0) {
180 		m_debugLines.remove(index);
181 	} else {
182 		qDebug() << "TextEditLineNumbers::unmarkDebugLine no marker at line " << line;
183 	}
184 	lineNumberArea->setDebugLines(m_debugLines);
185 	updateLineArea();
186 }
187 
setCurrentDebugLine(int line)188 void TextEditLineNumbers::setCurrentDebugLine(int line)
189 {
190 	lineNumberArea->setCurrentDebugLine(line);
191 	updateLineArea();
192 }
193 
updateLineArea(int)194 void TextEditLineNumbers::updateLineArea(int)
195 {
196 	lineNumberArea->update();
197 }
198 
updateLineArea()199 void TextEditLineNumbers::updateLineArea()
200 {
201 	lineNumberArea->update();
202 }
203 
setLineAreaColors(QColor foreground,QColor background)204 void TextEditLineNumbers::setLineAreaColors(QColor foreground, QColor background)
205 {
206     lineNumberArea->setColors(foreground, background);
207 }
208 
paintEvent(QPaintEvent *)209 void LineNumberArea::paintEvent(QPaintEvent *)
210 {
211     if ( !codeEditor->lineAreaVisble() )
212 		return;
213 	QPainter painter(this);
214     painter.fillRect(rect(), m_background);
215 	//code based partly on: http://john.nachtimwald.com/2009/08/15/qtextedit-with-line-numbers/
216 	QTextBlock block = codeEditor->document()->begin();
217 	int contents_y = codeEditor->verticalScrollBar()->value();
218 	int page_bottom = contents_y + codeEditor->viewport()->height();
219 	QTextBlock current_block = codeEditor->document()->findBlock(codeEditor->textCursor().position());
220 
221 	int line_count = 0;
222     painter.setPen(m_foreground); // not exactly black
223 
224     bool bold;
225 	QFont font = painter.font();
226     font.setPointSizeF(font.pointSizeF() * m_lineNumbersSizeFactor);
227     painter.setFont(font);
228     auto width = codeEditor->getAreaWidth();
229     auto metrics = codeEditor->fontMetrics();
230 
231     while (block.isValid()) {
232 		line_count += 1;
233 		QString number = QString::number(line_count);
234 		QPointF position = codeEditor->document()->documentLayout()->blockBoundingRect(block).topLeft();
235 		if (position.y() > page_bottom)
236 			break;
237         int y = round(position.y()) - contents_y + metrics.ascent();
238 		if (y>=0) { // the line is visible
239 			if (block == current_block) {
240 				bold = true;
241 				font.setBold(true);
242 				painter.setFont(font);
243 			}
244             int x = width - metrics.width(number)*m_lineNumbersSizeFactor - m_padding;
245             painter.drawText(x, y, number);
246 			if (bold) {
247 				font.setBold(false);
248 				painter.setFont(font);
249 			}
250 			if (m_debugLines.indexOf(line_count) >= 0) {
251 				painter.setPen(QColor(Qt::red));
252 				painter.setBrush(QColor(Qt::red));
253 				int height = codeEditor->fontMetrics().ascent() ;
254 				painter.drawEllipse(position.x(), y - height, height, height);
255 				painter.setBrush(QBrush());
256 				painter.setPen(QColor(Qt::darkGray).darker()); // not exactly black
257 			}
258 			if (m_currentDebugLine >= 0 && line_count == m_currentDebugLine) {
259 				QImage image("://themes/boring/gtk-media-play-ltr.png");
260 				painter.drawImage(position.x(), y, image);
261 			}
262 		}
263 		block = block.next();
264 	}
265 }
266