OSDN Git Service

Ver0.26
[gefu/Gefu.git] / hexview.cpp
1 #include "preferences.h"
2 #include "hexview.h"
3
4 #include <QApplication>
5 #include <QClipboard>
6 #include <QDebug>
7 #include <QPainter>
8 #include <QPaintEvent>
9 #include <QScrollArea>
10
11 const int HEXCHARS_IN_LINE = 3 * 16;
12 const int BYTES_PER_LINE = 16;
13 const int GAP_ADR_HEX = 1;
14 const int GAP_HEX_ASCII = 3;
15
16 ///////////////////////////////////////////////////////////////////////////////
17 /// \brief HexView::HexView
18 /// \param parent   親ウィジェット
19 ///
20 /// コンストラクタ
21 ///
22 HexView::HexView(QScrollArea *parent) :
23     QWidget(parent)
24 {
25     m_scrollArea = parent;
26     m_scrollArea->setWidget(this);
27     setObjectName("hexView");
28     setCursor(Qt::IBeamCursor);
29
30     resetSelection(0);
31 }
32
33 ///////////////////////////////////////////////////////////////////////////////
34 /// \brief HexView::setData
35 /// \param data データ
36 ///
37 /// データを設定します。
38 ///
39 void HexView::setData(const QByteArray &data)
40 {
41     m_data = data;
42     adjust();
43 }
44
45 ///////////////////////////////////////////////////////////////////////////////
46 /// \brief HexView::addressChars
47 /// \return アドレスエリアの表示桁数を返します。
48 ///
49 int HexView::addressChars() const
50 {
51     return QString("%1").arg(m_data.size(), 0, 16).length();
52 }
53
54 ///////////////////////////////////////////////////////////////////////////////
55 /// \brief HexView::adjust
56 ///
57 /// ウィジェットサイズを調整します。
58 ///
59 void HexView::adjust()
60 {
61     int lines = (m_data.size() / BYTES_PER_LINE) + 1;
62     setMinimumHeight((lines + 1) * m_charHeight);
63
64     int addressArea = (addressChars() + GAP_ADR_HEX) * m_charWidth;
65     int hexArea = HEXCHARS_IN_LINE * m_charWidth;
66     int asciiArea = (GAP_HEX_ASCII + BYTES_PER_LINE) * m_charWidth;
67     setMinimumWidth(addressArea + hexArea + asciiArea);
68 }
69
70 ///////////////////////////////////////////////////////////////////////////////
71 /// \brief HexView::cursorPos
72 /// \param pos  カーソル位置
73 /// \return カーソル位置に対応するデータインデックスを返します。
74 ///
75 int HexView::cursorPos(const QPoint &pos)
76 {
77     int result = -1;
78     if (xPosHex() <= pos.x() && pos.x() < xPosAscii()) {
79         int x = (pos.x() - xPosHex()) / m_charWidth;
80         if (x % 3 == 0) {
81             x /= 3;
82         }
83         else {
84             x = (x / 3) + 1;
85         }
86         int y = pos.y() / m_charHeight;
87
88         result = x + y * BYTES_PER_LINE;
89
90         qDebug() << x << y << result;
91     }
92
93     return result;
94 }
95
96 ///////////////////////////////////////////////////////////////////////////////
97 /// \brief HexView::resetSelection
98 /// \param index    データインデックス
99 ///
100 /// 選択範囲を初期化します。
101 ///
102 void HexView::resetSelection(int index)
103 {
104     m_selectionBegin = index;
105     m_selectionEnd = index;
106     m_selectionInit = index;
107
108     emit copyAvailable(false);
109 }
110
111 ///////////////////////////////////////////////////////////////////////////////
112 /// \brief HexView::setSelection
113 /// \param index    データインデックス
114 ///
115 /// 選択範囲を設定します。
116 ///
117 void HexView::setSelection(int index)
118 {
119     if (index > m_selectionInit) {
120         m_selectionBegin = m_selectionInit;
121         m_selectionEnd = index;
122     }
123     else {
124         m_selectionBegin = index;
125         m_selectionEnd = m_selectionInit;
126     }
127
128     emit copyAvailable(m_selectionBegin != m_selectionEnd);
129 }
130
131 ///////////////////////////////////////////////////////////////////////////////
132 /// \brief HexView::xPosHex
133 /// \return 16進表示エリアの開始座標を返します。
134 ///
135 int HexView::xPosHex() const
136 {
137     return m_charWidth * (addressChars() + GAP_ADR_HEX);
138 }
139
140 ///////////////////////////////////////////////////////////////////////////////
141 /// \brief HexView::xPosAscii
142 /// \return アスキー文字表示エリアの開始座標を返します。
143 ///
144 int HexView::xPosAscii() const
145 {
146     return xPosHex() + m_charWidth * (HEXCHARS_IN_LINE + GAP_HEX_ASCII);
147 }
148
149 ///////////////////////////////////////////////////////////////////////////////
150 /// \brief HexView::onCopy
151 ///
152 /// 選択範囲をクリップボードにコピーします。
153 ///
154 void HexView::onCopy()
155 {
156     QString selected;
157     for (int idx = m_selectionBegin; idx < m_selectionEnd; ++idx) {
158         quint8 c = static_cast<quint8>(m_data[idx]);
159         selected.append(QString("%1 ").arg(c, 2, 16, QChar('0')));
160     }
161
162     QClipboard *clipboard = qApp->clipboard();
163     clipboard->setText(selected.trimmed());
164 }
165
166 ///////////////////////////////////////////////////////////////////////////////
167 /// \brief HexView::onScaleDown
168 ///
169 /// 文字を小さくします。
170 ///
171 void HexView::onScaleDown()
172 {
173     Preferences prefs(this);
174     QFont font = prefs.getHexViewFont();
175     font.setPointSize(font.pointSize() - 1);
176     prefs.setHexViewFont(font);
177
178     setVisible(true);
179     adjust();
180     update();
181 }
182
183 ///////////////////////////////////////////////////////////////////////////////
184 /// \brief HexView::onScaleUp
185 ///
186 /// 文字を大きくします。
187 ///
188 void HexView::onScaleUp()
189 {
190     Preferences prefs(this);
191     QFont font = prefs.getHexViewFont();
192     font.setPointSize(font.pointSize() + 1);
193     prefs.setHexViewFont(font);
194
195     setVisible(true);
196     adjust();
197     update();
198 }
199
200 ///////////////////////////////////////////////////////////////////////////////
201 /// \brief HexView::onSelectAll
202 ///
203 /// すべて選択します。
204 ///
205 void HexView::onSelectAll()
206 {
207     resetSelection(0);
208     setSelection(m_data.size());
209
210     update();
211 }
212
213 ///////////////////////////////////////////////////////////////////////////////
214 /// \brief HexView::setVisible
215 /// \param visible  表示(true)/非表示(false)
216 ///
217 /// 表示状態になった場合の処理を行います。
218 ///
219 void HexView::setVisible(bool visible)
220 {
221     if (visible) {
222         Preferences prefs(this);
223         QPalette pal = this->palette();
224         pal.setColor(this->backgroundRole(), prefs.getHexViewBgColor());
225         pal.setColor(this->foregroundRole(), prefs.getHexViewFgColor());
226         this->setPalette(pal);
227         this->setAutoFillBackground(true);
228         this->setFont(prefs.getHexViewFont());
229
230         m_charHeight = fontMetrics().height();
231         m_charWidth = fontMetrics().width('9');
232     }
233
234     QWidget::setVisible(visible);
235 }
236
237 ///////////////////////////////////////////////////////////////////////////////
238 /// \brief HexView::mousePressEvent
239 /// \param e    マウスイベントオブジェクト
240 ///
241 /// マウスクリック時の処理を行います。
242 ///
243 void HexView::mousePressEvent(QMouseEvent *e)
244 {
245     if (e->button() == Qt::LeftButton) {
246         int cPos = cursorPos(e->pos());
247         resetSelection(cPos);
248
249         update();
250     }
251 }
252
253 ///////////////////////////////////////////////////////////////////////////////
254 /// \brief HexView::mouseDoubleClickEvent
255 /// \param e    マウスイベントオブジェクト
256 ///
257 /// ダブルクリック時の処理を行います。
258 ///
259 void HexView::mouseDoubleClickEvent(QMouseEvent *e)
260 {
261     if (e->button() == Qt::LeftButton) {
262         int cPos = cursorPos(e->pos());
263         int lineHead = (cPos / BYTES_PER_LINE) * BYTES_PER_LINE;
264         resetSelection(lineHead);
265         setSelection(lineHead + BYTES_PER_LINE);
266
267         update();
268     }
269 }
270
271 ///////////////////////////////////////////////////////////////////////////////
272 /// \brief HexView::mouseMoveEvent
273 /// \param e    マウスイベントオブジェクト
274 ///
275 /// マウス移動時の処理を行います。
276 ///
277 void HexView::mouseMoveEvent(QMouseEvent *e)
278 {
279     m_scrollArea->ensureVisible(e->x(), e->y());
280
281     int cPos = cursorPos(e->pos());
282     if (cPos != -1) {
283         setSelection(cPos);
284         update();
285     }
286 }
287
288 ///////////////////////////////////////////////////////////////////////////////
289 /// \brief HexView::paintEvent
290 /// \param e    描画イベントオブジェクト
291 ///
292 /// 描画イベントを処理します。
293 ///
294 void HexView::paintEvent(QPaintEvent *e)
295 {
296     QPainter painter(this);
297
298     // 描画対象となるデータの範囲
299     int firstLineIdx = ((e->rect().top() / m_charHeight) - m_charHeight) * BYTES_PER_LINE;
300     if (firstLineIdx < 0)
301         firstLineIdx = 0;
302     int lastLineIdx = ((e->rect().bottom() / m_charHeight) + m_charHeight) * BYTES_PER_LINE;
303     if (lastLineIdx > m_data.size())
304         lastLineIdx = m_data.size();
305     // 描画開始位置
306     int yPosStart = (firstLineIdx / BYTES_PER_LINE) * m_charHeight + m_charHeight;
307
308     // アドレスエリア
309     QRect addressRect(e->rect());
310     addressRect.setLeft(0);
311     addressRect.setWidth(m_charWidth * addressChars());
312     painter.fillRect(addressRect, Qt::gray);
313     painter.setPen(Qt::black);
314     for (int lineIdx = firstLineIdx, yPos = yPosStart;
315          lineIdx < lastLineIdx;
316          lineIdx += BYTES_PER_LINE, yPos += m_charHeight)
317     {
318         QString address = QString("%1")
319                 .arg(lineIdx, addressChars(), 16, QChar('0'));
320         painter.drawText(addressRect.left(), yPos, address);
321     }
322
323     // バイナリエリア
324     for (int lineIdx = firstLineIdx, yPos = yPosStart;
325          lineIdx < lastLineIdx;
326          lineIdx += BYTES_PER_LINE, yPos += m_charHeight)
327     {
328         int xPos = xPosHex();
329         for (int colIdx = 0;
330              lineIdx + colIdx < m_data.size() && colIdx < BYTES_PER_LINE;
331              colIdx++, xPos += m_charWidth * 3)
332         {
333             int Idx = lineIdx + colIdx;
334             if (m_selectionBegin <= Idx && Idx < m_selectionEnd) {
335                 painter.setBackground(this->palette().highlight());
336                 painter.setBackgroundMode(Qt::OpaqueMode);
337                 painter.setPen(this->palette().highlightedText().color());
338             }
339             else {
340                 painter.setPen(this->palette().color(this->foregroundRole()));
341                 painter.setBackgroundMode(Qt::TransparentMode);
342             }
343
344             quint8 ch = static_cast<quint8>(m_data[Idx]);
345             QString s = QString("%1").arg(ch, 2, 16, QChar('0'));
346             if (colIdx < 8) {
347                 s.append(" ");
348                 painter.drawText(xPos, yPos, s);
349             }
350             else {
351                 s.prepend(" ");
352                 painter.drawText(xPos, yPos, s);
353             }
354         }
355     }
356
357     // アスキーエリア
358     painter.setPen(this->palette().color(this->foregroundRole()));
359     painter.setBackgroundMode(Qt::TransparentMode);
360     for (int lineIdx = firstLineIdx, yPos = yPosStart;
361          lineIdx < lastLineIdx;
362          lineIdx += BYTES_PER_LINE, yPos += m_charHeight)
363     {
364         int xPos = xPosAscii();
365
366         for (int colIdx = 0;
367              lineIdx + colIdx < m_data.size() && colIdx < BYTES_PER_LINE;
368              colIdx++, xPos += m_charWidth)
369         {
370             quint8 ch = static_cast<quint8>(m_data[lineIdx + colIdx]);
371             if (!::isprint(ch) && ch != ' ') {
372                 ch = '.';
373             }
374             QString s = QString(ch).toLatin1();
375             painter.drawText(xPos, yPos, s);
376         }
377     }
378 }