OSDN Git Service

Ver0.13
[gefu/Gefu.git] / simpletextview.cpp
1 #include "common.h"
2 #include "simpletextview.h"
3 #include "mainwindow.h"
4
5 #include <QDebug>
6 #include <QMenu>
7 #include <QSettings>
8 #include <QShortcut>
9 #include <QTextCodec>
10 #include <QStatusBar>
11
12 QString KeyEventToSequence(const QKeyEvent *event)
13 {
14     QString modifier = QString::null;
15     if (event->modifiers() & Qt::ShiftModifier)     { modifier += "Shift+"; }
16     if (event->modifiers() & Qt::ControlModifier)   { modifier += "Ctrl+"; }
17     if (event->modifiers() & Qt::AltModifier)       { modifier += "Alt+"; }
18     if (event->modifiers() & Qt::MetaModifier)      { modifier += "Meta+"; }
19
20     QString key = QKeySequence(event->key()).toString();
21     return QKeySequence(modifier + key).toString();
22 }
23
24 SimpleTextView::SimpleTextView(QWidget *parent) :
25     QPlainTextEdit(parent),
26     m_convEUC(NULL),
27     m_convJIS(NULL),
28     m_convSJIS(NULL),
29     m_convUTF8(NULL),
30     m_convUTF16BE(NULL),
31     m_convUTF16LE(NULL),
32     m_copy(NULL),
33     m_back(NULL)
34 {
35     setReadOnly(true);
36     updateAppearance();
37
38     setContextMenuPolicy(Qt::DefaultContextMenu);
39     m_convEUC = new QAction(tr("EUC-JPで再読込"), this);
40     m_convJIS = new QAction(tr("ISO 2022-JP(JIS)で再読込"), this);
41     m_convSJIS = new QAction(tr("Shift-JISで再読込"), this);
42     m_convUTF8 = new QAction(tr("UTF-8で再読込"), this);
43     m_convUTF16 = new QAction(tr("UTF-16で再読込"), this);
44     m_convUTF16BE = new QAction(tr("UTF-16BEで再読込"), this);
45     m_convUTF16LE = new QAction(tr("UTF-16LEで再読込"), this);
46     m_copy = new QAction(tr("選択範囲をクリップボードにコピー"), this);
47     m_back = new QAction(tr("戻る"), this);
48
49     m_convEUC->setShortcut(QKeySequence("Shift+E"));
50     m_convJIS->setShortcut(QKeySequence("Shift+J"));
51     m_convSJIS->setShortcut(QKeySequence("Shift+S"));
52     m_convUTF8->setShortcut(QKeySequence("Shift+U"));
53     m_convUTF16->setShortcut(QKeySequence("Shift+I"));
54     m_convUTF16BE->setShortcut(QKeySequence("Shift+O"));
55     m_convUTF16LE->setShortcut(QKeySequence("Shift+P"));
56     m_copy->setShortcut(QKeySequence::Copy);
57     m_back->setShortcut(QKeySequence("Return"));
58
59     connect(m_convEUC, SIGNAL(triggered()), this, SLOT(convertFromEUC()));
60     connect(m_convJIS, SIGNAL(triggered()), this, SLOT(convertFromJIS()));
61     connect(m_convSJIS, SIGNAL(triggered()), this, SLOT(convertFromSJIS()));
62     connect(m_convUTF8, SIGNAL(triggered()), this, SLOT(convertFromUTF8()));
63     connect(m_convUTF16, SIGNAL(triggered()), this, SLOT(convertFromUTF16()));
64     connect(m_convUTF16BE, SIGNAL(triggered()), this, SLOT(convertFromUTF16BE()));
65     connect(m_convUTF16LE, SIGNAL(triggered()), this, SLOT(convertFromUTF16LE()));
66     connect(m_copy, SIGNAL(triggered()), this, SLOT(copy()));
67     connect(m_back, SIGNAL(triggered()), this, SLOT(back()));
68
69     connect(this, SIGNAL(copyAvailable(bool)), this, SLOT(onCopyAvailable(bool)));
70
71     m_copy->setEnabled(false);
72 }
73
74 void SimpleTextView::setSource(const QByteArray &source)
75 {
76     m_source = source;
77
78     // BOMで文字コードを判別する
79     const char UTF8_BOM[] = { 0xEF, 0xBB, 0xBF };
80     const char UTF16BE_BOM[] = { 0xFE, 0xFF };
81     const char UTF16LE_BOM[] = { 0xFF, 0xFE };
82
83     if (m_source.indexOf(QByteArray(UTF8_BOM)) == 0) {
84         qDebug() << "Detect UTF-8 BOM";
85         convertFromUTF8();
86     }
87     else if (m_source.indexOf(QByteArray(UTF16BE_BOM)) == 0 ||
88              m_source.indexOf(QByteArray(UTF16LE_BOM)) == 0)
89     {
90         qDebug() << "Detect UTF-16 BOM";
91         convertFromUTF16();
92     }
93
94     QByteArray first1KB = m_source.left(1024);
95     // 文字コードを示す文字列で判別する
96     QTextCodec *codec = QTextCodec::codecForName("UTF-8");
97     QString text = codec->toUnicode(first1KB).toLower();
98     if (text.indexOf("utf8") != -1 || text.indexOf("utf-8") != -1) {
99         convertFromUTF8();
100         return;
101     }
102     if (text.indexOf("sjis") != -1 || text.indexOf("shift-jis") != -1 ||
103         text.indexOf("shift_jis") != -1)
104     {
105         convertFromSJIS();
106         return;
107     }
108     if (text.indexOf("euc") != -1 || text.indexOf("euc-jp") != -1 ||
109         text.indexOf("euc_jp") != -1)
110     {
111         convertFromEUC();
112         return;
113     }
114     if (text.indexOf("jis") != -1 || text.indexOf("iso 2022-jp") != -1) {
115         convertFromJIS();
116         return;
117     }
118
119     std::string code = detectCode(first1KB);
120     codec = QTextCodec::codecForName(code.c_str());
121     setPlainText(codec->toUnicode(m_source));
122     getMainWnd()->statusBar()->showMessage(code.c_str());
123 }
124
125 void SimpleTextView::updateAppearance()
126 {
127     QSettings settings;
128
129     QPalette pal = this->palette();
130     if (settings.value(IniKey_ViewerInherit).toBool()) {
131         pal.setColor(QPalette::Base,
132                      settings.value(IniKey_ViewColorBgNormal).value<QColor>());
133         pal.setColor(QPalette::Text,
134                      settings.value(IniKey_ViewColorFgNormal).value<QColor>());
135     }
136     else {
137         pal.setColor(QPalette::Base,
138                      settings.value(IniKey_ViewerColorBg).value<QColor>());
139         pal.setColor(QPalette::Text,
140                      settings.value(IniKey_ViewerColorFg).value<QColor>());
141     }
142     setPalette(pal);
143     setFont(settings.value(IniKey_ViewerFont).value<QFont>());
144 }
145
146 void SimpleTextView::convertFromEUC()
147 {
148     QTextCodec *codec = QTextCodec::codecForName("EUC-JP");
149     setPlainText(codec->toUnicode(m_source));
150     getMainWnd()->statusBar()->showMessage("EUC-JP");
151 }
152
153 void SimpleTextView::convertFromJIS()
154 {
155     QTextCodec *codec = QTextCodec::codecForName("ISO 2022-JP");
156     setPlainText(codec->toUnicode(m_source));
157     getMainWnd()->statusBar()->showMessage("ISO 2022-JP");
158 }
159
160 void SimpleTextView::convertFromSJIS()
161 {
162     QTextCodec *codec = QTextCodec::codecForName("Shift-JIS");
163     setPlainText(codec->toUnicode(m_source));
164     getMainWnd()->statusBar()->showMessage("Shift-JIS");
165
166 }
167
168 void SimpleTextView::convertFromUTF8()
169 {
170     QTextCodec *codec = QTextCodec::codecForName("UTF-8");
171     setPlainText(codec->toUnicode(m_source));
172     getMainWnd()->statusBar()->showMessage("UTF-8");
173 }
174
175 void SimpleTextView::convertFromUTF16()
176 {
177     QTextCodec *codec = QTextCodec::codecForName("UTF-16");
178     setPlainText(codec->toUnicode(m_source));
179     getMainWnd()->statusBar()->showMessage("UTF-16");
180 }
181
182 void SimpleTextView::convertFromUTF16BE()
183 {
184     QTextCodec *codec = QTextCodec::codecForName("UTF-16BE");
185     setPlainText(codec->toUnicode(m_source));
186     getMainWnd()->statusBar()->showMessage("UTF-16BE");
187 }
188
189 void SimpleTextView::convertFromUTF16LE()
190 {
191     QTextCodec *codec = QTextCodec::codecForName("UTF-16LE");
192     setPlainText(codec->toUnicode(m_source));
193     getMainWnd()->statusBar()->showMessage("UTF-16LE");
194 }
195
196 void SimpleTextView::onCopyAvailable(bool yes)
197 {
198     m_copy->setEnabled(yes);
199 }
200
201 void SimpleTextView::back()
202 {
203     emit viewFinished(this);
204 }
205
206 void SimpleTextView::keyPressEvent(QKeyEvent *event)
207 {
208     QString ksq = KeyEventToSequence(event);
209
210     if (ksq == "Return" || ksq == "Backspace" || ksq == "W") {
211         emit viewFinished(this);
212         event->accept();
213         return;
214     }
215
216     if (!ksq.isEmpty()) {
217         foreach (QObject *obj, this->children()) {
218             QAction *action = qobject_cast<QAction*>(obj);
219             if (action && action->isEnabled()) {
220                 foreach (const QKeySequence &keySeq, action->shortcuts()) {
221                     if (ksq == keySeq.toString()) {
222                         qDebug() << "emit " << ksq << " " << action->objectName();
223                         emit action->triggered();
224                         event->accept();
225                         return;
226                     }
227                 }
228             }
229         }
230
231         foreach (QObject *obj, getMainWnd()->children()) {
232             QAction *action = qobject_cast<QAction*>(obj);
233             if (action && action->isEnabled()) {
234                 foreach (const QKeySequence &keySeq, action->shortcuts()) {
235                     if (ksq == keySeq.toString()) {
236                         qDebug() << "emit " << ksq << " " << action->objectName();
237                         emit action->triggered();
238                         event->accept();
239                         return;
240                     }
241                 }
242             }
243         }
244     }
245
246     if (ksq != "Down" && ksq != "Up") {
247         qDebug() << ksq;
248     }
249
250     QPlainTextEdit::keyPressEvent(event);
251 }
252
253
254 void SimpleTextView::contextMenuEvent(QContextMenuEvent *event)
255 {
256     qDebug() << "contextMenuEvent();";
257     QMenu menu(this);
258     menu.addAction(m_convEUC);
259     menu.addAction(m_convJIS);
260     menu.addAction(m_convSJIS);
261     menu.addAction(m_convUTF8);
262     menu.addAction(m_convUTF16BE);
263     menu.addAction(m_convUTF16LE);
264     menu.addSeparator();
265     menu.addAction(m_copy);
266     menu.addSeparator();
267     menu.addAction(m_back);
268     menu.exec(event->globalPos());
269 }
270
271 // http://dobon.net/vb/dotnet/string/detectcode.html より拝借
272 std::string SimpleTextView::detectCode(const QByteArray &bytes)
273 {
274     typedef unsigned char byte;
275     const byte bEscape = 0x1B;
276     const byte bAt = 0x40;
277     const byte bDollar = 0x24;
278     const byte bAnd = 0x26;
279     const byte bOpen = 0x28;    //'('
280     const byte bB = 0x42;
281     const byte bD = 0x44;
282     const byte bJ = 0x4A;
283     const byte bI = 0x49;
284
285     int len = bytes.size();
286     byte b1, b2, b3, b4;
287
288     bool isBinary = false;
289     for (int i = 0; i < len; i++) {
290         b1 = bytes[i];
291         if (b1 <= 0x06 || b1 == 0x7F || b1 == 0xFF) {
292             //'binary'
293             isBinary = true;
294             if (b1 == 0x00 && i < len - 1 && static_cast<byte>(bytes[i + 1]) <= 0x7F) {
295                 return "UTF-16LE";
296             }
297         }
298     }
299     if (isBinary) {
300         return "UTF-8";
301     }
302
303     bool notJapanese = true;
304     for (int i = 0; i < len; i++) {
305         b1 = bytes[i];
306         if (b1 == bEscape || 0x80 <= b1) {
307             notJapanese = false;
308             break;
309         }
310     }
311     if (notJapanese) {
312         return "UTF-8";
313     }
314
315     for (int i = 0; i < len - 2; i++) {
316         b1 = bytes[i];
317         b2 = bytes[i + 1];
318         b3 = bytes[i + 2];
319
320         if (b1 == bEscape){
321             if ((b2 == bDollar && b3 == bAt) ||
322                 (b2 == bDollar && b3 == bB) ||
323                 (b2 == bOpen && (b3 == bB || b3 == bJ)) ||
324                 (b2 == bOpen && b3 == bI))
325             {
326                 return "ISO 2022-JP";
327             }
328             if (i < len - 3) {
329                 b4 = bytes[i + 3];
330                 if (b2 == bDollar && b3 == bOpen && b4 == bD) {
331                     return "ISO 2022-JP";
332                 }
333                 if (i < len - 5 &&
334                     b2 == bAnd && b3 == bAt && b4 == bEscape &&
335                     bytes[i + 4] == bDollar && bytes[i + 5] == bB)
336                 {
337                     return "ISO 2022-JP";
338                 }
339             }
340         }
341     }
342
343     int sjis = 0;
344     int euc = 0;
345     int utf8 = 0;
346     for (int i = 0; i < len - 1; i++) {
347         b1 = bytes[i];
348         b2 = bytes[i + 1];
349         if (((0x81 <= b1 && b1 <= 0x9F) || (0xE0 <= b1 && b1 <= 0xFC)) &&
350             ((0x40 <= b2 && b2 <= 0x7E) || (0x80 <= b2 && b2 <= 0xFC)))
351         {
352             sjis += 2;
353             i++;
354         }
355     }
356     for (int i = 0; i < len - 1; i++) {
357         b1 = bytes[i];
358         b2 = bytes[i + 1];
359         if (((0xA1 <= b1 && b1 <= 0xFE) && (0xA1 <= b2 && b2 <= 0xFE)) ||
360             (b1 == 0x8E && (0xA1 <= b2 && b2 <= 0xDF)))
361         {
362             euc += 2;
363             i++;
364         }
365         else if (i < len - 2) {
366             b3 = bytes[i + 2];
367             if (b1 == 0x8F && (0xA1 <= b2 && b2 <= 0xFE) &&
368                 (0xA1 <= b3 && b3 <= 0xFE))
369             {
370                 euc += 3;
371                 i += 2;
372             }
373         }
374     }
375     for (int i = 0; i < len - 1; i++) {
376         b1 = bytes[i];
377         b2 = bytes[i + 1];
378         if ((0xC0 <= b1 && b1 <= 0xDF) && (0x80 <= b2 && b2 <= 0xBF)) {
379             utf8 += 2;
380             i++;
381         }
382         else if (i < len - 2) {
383             b3 = bytes[i + 2];
384             if ((0xE0 <= b1 && b1 <= 0xEF) && (0x80 <= b2 && b2 <= 0xBF) &&
385                 (0x80 <= b3 && b3 <= 0xBF))
386             {
387                 utf8 += 3;
388                 i += 2;
389             }
390         }
391     }
392
393     if (euc > sjis && euc > utf8) {
394         return "EUC-JP";
395     }
396     else if (sjis > euc && sjis > utf8) {
397         return "Shift-JIS";
398     }
399     else if (utf8 > euc && utf8 > sjis) {
400         return "UTF-8";
401     }
402
403 #ifdef Q_OS_WIN
404     return "Shift-JIS";
405 #else
406     return "UTF-8";
407 #endif
408 }