OSDN Git Service

環境設定ダイアログのタブオーダーを修正
[gefu/Gefu.git] / textview.cpp
1 #include "global.h"
2 #include "preferences.h"
3 #include "textview.h"
4
5 #include <QApplication>
6 #include <QClipboard>
7 #include <QDebug>
8 #include <QPainter>
9 #include <QPaintEvent>
10 #include <QScrollArea>
11 #include <QTextCodec>
12
13 const int GAP_LINE_TEXT = 1;
14 const int MARGIN_RIGHT = 1;
15
16 TextView::TextView(QScrollArea *parent) :
17     QWidget(parent)
18 {
19     m_scrollArea = parent;
20     m_scrollArea->setWidget(this);
21     setObjectName("textView");
22
23     resetSelection(0);
24 }
25
26 void TextView::setData(const QByteArray &data)
27 {
28     m_source = data;
29
30     std::string code = detectCode(data.left(1024));
31     convertFrom(code.c_str());
32 }
33
34 void TextView::adjust()
35 {
36     // 行数をカウントする
37     int lineNum = 1;
38     int offset = 0;
39     for (; offset < m_data.size(); ++offset) {
40         if (m_data[offset] == '\r') {
41             if (offset + 1 < m_data.size() && m_data[offset + 1] == '\n') {
42                 ++offset;
43             }
44             ++lineNum;
45         }
46         else if (m_data[offset] == '\n') {
47             ++lineNum;
48         }
49     }
50
51     // 行番号表示に必要な幅を取得する
52     int lineNumWidth = m_charWidth * lineNumChars(lineNum);
53     // テキストの表示開始位置
54     int xPosText = lineNumWidth + GAP_LINE_TEXT * m_charWidth;
55
56     setMinimumWidth(xPosText + m_charWidth * 4);
57
58     // 文字の表示位置を取得する
59     m_viewPositions.clear();
60     int x = 0;
61     int y = m_charHeight;
62     int lineWidth = 0;
63     int wrapLine = 0;
64     const int lineMaxWidth = width() - xPosText - MARGIN_RIGHT * m_charWidth;
65     for (lineNum = 1, offset = 0; offset < m_data.size(); ++offset) {
66         bool isEOL = false;
67         if (m_data[offset] == '\r') {
68             isEOL = true;
69             if (offset < m_data.size() - 1 && m_data[offset + 1] == '\n') {
70                 ++offset;
71             }
72         }
73         if (m_data[offset] == '\n') {
74             isEOL = true;
75         }
76
77         ViewPosition vPos;
78         vPos.lineNum = lineNum;
79         vPos.offset = offset;
80
81         int charWidth = fontMetrics().width(m_data.mid(offset, 1));
82         int tabIndent = 0;
83         if (isEOL) {
84             charWidth = fontMetrics().width(0x21B5);
85         }
86         else if (m_data[offset] == '\t') {
87             tabIndent = ((lineWidth + m_tabWidth) / m_tabWidth) * m_tabWidth - lineWidth;
88             charWidth = fontMetrics().width("^");
89         }
90
91         if (x + charWidth >= lineMaxWidth) {
92             x = 0;
93             ++wrapLine;
94         }
95
96         vPos.x = xPosText + x;
97         vPos.y = y + wrapLine * m_charHeight;
98         m_viewPositions << vPos;
99
100         if (isEOL) {
101             lineWidth = 0;
102             x = 0;
103             y += (wrapLine + 1) * m_charHeight;
104             wrapLine = 0;
105             ++lineNum;
106         }
107         else {
108             if (m_data[offset] == '\t') {
109                 x += tabIndent;
110                 if (x > lineMaxWidth) {
111                     x = x - lineMaxWidth;
112                     ++wrapLine;
113                 }
114             }
115             else {
116                 x += charWidth;
117             }
118             lineWidth += charWidth + tabIndent;
119         }
120     }
121
122     if (!m_viewPositions.isEmpty()) {
123         setMinimumHeight(m_viewPositions.last().y + m_charHeight);
124     }
125 }
126
127 void TextView::convertFrom(const char *code)
128 {
129     QTextCodec *codec = QTextCodec::codecForName(code);
130     m_data = codec->toUnicode(m_source);
131     adjust();
132     resetSelection(0);
133     update();
134
135     emit statusChanged(code);
136 }
137
138 int TextView::cursorPos(const QPoint &pos)
139 {
140     int n;
141     int y;
142     for (n = 0; n < m_viewPositions.size(); n++) {
143         if (pos.y() < m_viewPositions[n].y) {
144             y = m_viewPositions[n].y;
145             break;
146         }
147     }
148
149     for(; n < m_viewPositions.size(); n++) {
150         if (pos.x() < m_viewPositions[n].x) {
151             return n - 1;
152         }
153         if (y < m_viewPositions[n].y) {
154             return n;
155         }
156     }
157
158     return n;
159 }
160
161 int TextView::lineNumChars(int lines) const
162 {
163     if (lines == -1) {
164         if (m_viewPositions.isEmpty()) {
165             return 0;
166         }
167         lines = m_viewPositions.last().lineNum;
168     }
169     return QString("%1").arg(lines).size() + 1;
170 }
171
172 void TextView::resetSelection(int index)
173 {
174     m_selectionInit = index;
175     m_selectionBegin = index;
176     m_selectionEnd = index;
177
178     emit copyAvailable(false);
179 }
180
181 void TextView::setSelection(int index)
182 {
183     if (index > m_selectionInit) {
184         m_selectionBegin = m_selectionInit;
185         m_selectionEnd = index;
186     }
187     else {
188         m_selectionBegin = index;
189         m_selectionEnd = m_selectionInit;
190     }
191
192     emit copyAvailable(m_selectionBegin != m_selectionEnd);
193 }
194
195 void TextView::onConvertFromEUC()
196 {
197     convertFrom("EUC-JP");
198 }
199
200 void TextView::onConvertFromJIS()
201 {
202     convertFrom("ISO 2022-JP");
203 }
204
205 void TextView::onConvertFromSJIS()
206 {
207     convertFrom("Shift-JIS");
208 }
209
210 void TextView::onConvertFromUTF8()
211 {
212     convertFrom("UTF-8");
213 }
214
215 void TextView::onConvertFromUTF16()
216 {
217     convertFrom("UTF-16");
218 }
219
220 void TextView::onConvertFromUTF16BE()
221 {
222     convertFrom("UTF-16BE");
223 }
224
225 void TextView::onConvertFromUTF16LE()
226 {
227     convertFrom("UTF-16LE");
228 }
229
230 void TextView::onCopy()
231 {
232     QString selected;
233     int length = m_viewPositions[m_selectionEnd].offset -
234             m_viewPositions[m_selectionBegin].offset;
235
236     selected = m_data.mid(m_viewPositions[m_selectionBegin].offset, length);
237
238     QClipboard *clipboard = qApp->clipboard();
239     clipboard->setText(selected);
240 }
241
242 void TextView::onScaleDown()
243 {
244     Preferences prefs(this);
245     QFont font = prefs.getTextViewFont();
246     font.setPointSize(font.pointSize() - 1);
247     prefs.setTextViewFont(font);
248
249     setVisible(true);
250     adjust();
251     update();
252 }
253
254 void TextView::onScaleUp()
255 {
256     Preferences prefs(this);
257     QFont font = prefs.getTextViewFont();
258     font.setPointSize(font.pointSize() + 1);
259     prefs.setTextViewFont(font);
260
261     setVisible(true);
262     adjust();
263     update();
264 }
265
266 void TextView::onSelectAll()
267 {
268     m_selectionInit = 0;
269     m_selectionBegin = 0;
270     m_selectionEnd = m_viewPositions.size();
271 }
272
273 void TextView::setVisible(bool visible)
274 {
275     if (visible) {
276         Preferences prefs(this);
277         QPalette pal = this->palette();
278         pal.setColor(this->backgroundRole(), prefs.getTextViewBgColor());
279         pal.setColor(this->foregroundRole(), prefs.getTextViewFgColor());
280         pal.setBrush(QPalette::BrightText, prefs.getTextViewCtrlColor());
281         this->setPalette(pal);
282         this->setAutoFillBackground(true);
283         this->setFont(prefs.getTextViewFont());
284
285         m_charHeight = fontMetrics().height();
286         m_charWidth = fontMetrics().width('9');
287         m_tabWidth = 8 * m_charWidth;
288     }
289
290     QWidget::setVisible(visible);
291 }
292
293 void TextView::mousePressEvent(QMouseEvent *e)
294 {
295     if (e->button() == Qt::LeftButton) {
296         int cPos = cursorPos(e->pos());
297         resetSelection(cPos);
298
299         update();
300     }
301 }
302
303 void TextView::mouseDoubleClickEvent(QMouseEvent *e)
304 {
305     if (e->button() == Qt::LeftButton) {
306         int cPos = cursorPos(e->pos());
307         if (cPos >= m_viewPositions.size()) {
308             cPos = m_viewPositions.size() - 1;
309         }
310         int y = m_viewPositions[cPos].y;
311
312         int n;
313         for (n = cPos; n >= 0; n--) {
314             if (m_viewPositions[n].y != y) {
315                 ++n;
316                 break;
317             }
318         }
319         resetSelection(n);
320
321         for (n = cPos; n < m_viewPositions.size(); n++) {
322             if (m_viewPositions[n].y != y) {
323                 break;
324             }
325         }
326         setSelection(n - 1);
327
328         update();
329     }
330 }
331
332 void TextView::mouseMoveEvent(QMouseEvent *e)
333 {
334     m_scrollArea->ensureVisible(e->x(), e->y());
335
336     int cPos = cursorPos(e->pos());
337     setSelection(cPos);
338     update();
339 }
340
341 void TextView::paintEvent(QPaintEvent *e)
342 {
343     QPainter painter(this);
344
345     // 行番号エリア
346     QRect lineNumRect(e->rect());
347     lineNumRect.setLeft(0);
348     lineNumRect.setWidth(m_charWidth * lineNumChars());
349     painter.fillRect(lineNumRect, Qt::gray);
350
351     int prevLine = -1;
352     int idx;
353     for (idx = 0;
354          idx < m_viewPositions.size() &&
355             m_viewPositions[idx].y < e->rect().top();
356          ++idx)
357     {
358         prevLine = m_viewPositions[idx].lineNum;
359     }
360
361     painter.setPen(this->palette().color(this->foregroundRole()));
362     for (;
363          idx < m_viewPositions.size() &&
364             m_viewPositions[idx].y <= e->rect().bottom() + m_charHeight;
365          ++idx)
366     {
367         const ViewPosition &vPos = m_viewPositions[idx];
368
369         // 行番号
370         if (vPos.lineNum != prevLine) {
371             prevLine = vPos.lineNum;
372             painter.setPen(Qt::black);
373             painter.setBackgroundMode(Qt::TransparentMode);
374             QString lineNumber = QString("%1").arg(vPos.lineNum);
375             painter.drawText(
376                         lineNumRect.right() - fontMetrics().width(lineNumber),
377                         vPos.y,
378                         lineNumber);
379         }
380
381         if (m_selectionBegin <= idx && idx < m_selectionEnd) {
382             painter.setBackground(this->palette().highlight());
383             painter.setBackgroundMode(Qt::OpaqueMode);
384             painter.setPen(this->palette().highlightedText().color());
385         }
386         else {
387             painter.setPen(this->palette().color(this->foregroundRole()));
388             painter.setBackgroundMode(Qt::TransparentMode);
389         }
390
391         QString ch = m_data.mid(vPos.offset, 1);
392         if (ch == "\r" || ch == "\n") {
393             painter.setPen(this->palette().color(QPalette::BrightText));
394             painter.drawText(vPos.x, vPos.y, QChar(0x21B5));
395             painter.setPen(this->palette().color(this->foregroundRole()));
396         }
397         else if (ch == "\t") {
398             painter.setPen(this->palette().color(QPalette::BrightText));
399             painter.drawText(vPos.x, vPos.y, "^");
400             painter.setPen(this->palette().color(this->foregroundRole()));
401         }
402         else {
403             painter.drawText(vPos.x, vPos.y, ch);
404         }
405     }
406 }
407
408 void TextView::resizeEvent(QResizeEvent *e)
409 {
410     QWidget::resizeEvent(e);
411
412     adjust();
413 }