OSDN Git Service

6690dd7af8361100c7a38d77374c694249d6c796
[pheasant/hexedit.git] / src / control / standard / hexview.cpp
1
2 #include <QtGui>
3 #include <algorithm>
4 #include <vector>
5 #include "hexview.h"
6 #include "scursor.h"
7 #include "../document.h"
8 #include "../highlight.h"
9
10 using namespace std;
11
12 namespace Standard {
13
14 ////////////////////////////////////////
15 // Config
16 HexConfig::HexConfig()
17         : Margin(2, 2, 3, 3)
18         , ByteMargin(3, 0, 2, 0)
19         , Font("Monaco", 17)
20         , EnableCaret(true)
21         , CaretBlinkTime(500)
22         , FontMetrics(Font)
23 {
24         // Coloring
25         Colors[Color::Background] = QColor(0xEF,0xEF,0xEF);
26         Colors[Color::Text] = QColor(0,0,0);
27         Colors[Color::SelBackground] = QColor(0xA0,0xA0,0xFF);
28         Colors[Color::SelText] = QColor(0,0,0);
29         Colors[Color::CaretBackground] = QColor(0xFF, 0, 0);
30         Colors[Color::CaretText] = QColor(0xFF,0xFF,0xFF);
31
32         // Font
33         Font.setFixedPitch(true);
34
35         update();
36 }
37
38 void HexConfig::update()
39 {
40         // TODO: set ByteMargin value(left=charWidth/2, right=charWidth/2)
41
42         // Pos
43         x_begin[0] = Margin.left() + ByteMargin.left();
44         for (int i = 1; i < Num; i++) {
45                 x_begin[i] = x_begin[i-1] + byteWidth();
46         }
47
48         // Pos of end
49         for (int i = 0; i < Num; i++) {
50                 x_end[i] = x_begin[i] + charWidth(2) + ByteMargin.right();
51         }
52
53         // Area
54         x_area[0] = Margin.left() + ByteMargin.left();
55         for (int i = 1; i < Num; i++) {
56                 x_area[i] = x_area[i-1] + byteWidth();
57         }
58         x_area[Num] = x_area[Num-1] + byteWidth();
59 }
60
61 int HexConfig::drawableLines(int height) const
62 {
63         const int y = top() + byteMargin().top();
64         return (height - y + byteHeight()) / byteHeight();
65 }
66
67 int HexConfig::XToPos(int x) const
68 {
69         if (x < Margin.left()) {
70                 return -1;
71         }
72
73         return (int)distance(x_area, lower_bound(x_area, x_area + Num + 1, x)) - 1;
74 }
75
76 int HexConfig::YToLine(int y) const
77 {
78         if (y < top()) {
79                 return -1;
80         }
81         return (y - top()) / byteHeight();
82 }
83
84 ////////////////////////////////////////
85 // View
86
87 HexView::HexView(QWidget *parent, Document *doc, Highlight *hi)
88         : ::View(parent, doc, hi)
89         , cursor_(new Cursor(doc, this))
90 {
91         // Enable keyboard input
92         setFocusPolicy(Qt::WheelFocus);
93 }
94
95 void HexView::resizeEvent(QResizeEvent *rs)
96 {
97         QSize size(qMin(rs->size().width(), config_.maxWidth()), rs->size().height());
98         QResizeEvent resize(size, rs->oldSize());
99         View::resizeEvent(&resize);
100         pix_.fill(config_.Colors[Color::Background]);
101         drawView();
102 }
103
104 void HexView::drawView(DrawMode mode, int line_start, int end)
105 {
106         //qDebug("refresh event mode:%d line:%d end:%d", mode, line_start, end);
107         //qDebug(" pos:%llu, anchor:%llu", cursor_->Position, cursor_->PositionAnchor);
108
109         // FIXME: refactoring refresh event
110         QPainter painter;
111         painter.begin(&pix_);
112         painter.setFont(config_.Font);
113
114         if (!document_->length()) {
115                 // TODO: draw Empty Background only
116                 QBrush brush(config_.Colors[Color::Background]);
117                 painter.fillRect(0, 0, width(), height(), brush);
118                 painter.end();
119                 // Update screen buffer
120                 update(0, 0, width(), height());
121                 return;
122         }
123
124         Q_ASSERT(0 <= line_start);
125         Q_ASSERT(static_cast<uint>(line_start) <= document_->length() / HexConfig::Num + 1);
126         Q_ASSERT(0 <= end);
127         Q_ASSERT(static_cast<uint>(end) <= document_->length() / HexConfig::Num + 1);
128
129         // Get draw range
130         int y_top = config_.top();
131         int y = config_.top() + config_.byteMargin().top();
132         int count_draw_line, max_y;
133
134         // Get minumum drawing area
135         switch (mode) {
136         case DRAW_ALL:
137                 count_draw_line = config_.drawableLines(height());
138                 break;
139         case DRAW_LINE:
140                 y_top += config_.byteHeight() * line_start;
141                 y     += config_.byteHeight() * line_start;
142                 count_draw_line = 1;
143                 break;
144         case DRAW_AFTER:
145                 y_top += config_.byteHeight() * line_start;
146                 y     += config_.byteHeight() * line_start;
147                 max_y = qMax(y + config_.byteHeight(), height());
148                 count_draw_line = config_.drawableLines(max_y - y);
149                 break;
150         case DRAW_RANGE:
151                 y_top += config_.byteHeight() * line_start;
152                 y     += config_.byteHeight() * line_start;
153                 max_y = qMin(y + config_.byteHeight() * end, height());
154                 count_draw_line = config_.drawableLines(max_y - y);
155                 break;
156         }
157
158         // Get top position of view
159         const quint64 top = (cursor_->Top + line_start) * HexConfig::Num;
160         const uint size = qMin(document_->length() - top, (quint64)HexConfig::Num * count_draw_line);
161         if (size == 0) {
162                 return;
163         }
164
165         // Draw empty area(after end line)
166         if (mode == DRAW_ALL || mode == DRAW_AFTER) {
167                 //qDebug("draw empty area DRAW_ALL or DRAW_AFTER");
168                 QBrush brush(config_.Colors[Color::Background]);
169                 const int y_start = y_top + qMax(0, count_draw_line - 1) * config_.byteHeight();
170                 painter.fillRect(0, y_start, width(), height(), brush);
171         }
172
173         // Copy from document
174         if (buff_.capacity() < size) {
175                 buff_.resize(size);
176         }
177         document_->get(top, &buff_[0], size);
178
179         // Get selectead area
180         bool selected = false;
181         quint64 sel_begin = 0, sel_end = 0;
182         isSelected(selected, sel_begin, sel_end, top, count_draw_line, size);
183
184         // TODO: Adding cache class for color highligh data
185         ::DrawInfo di(y, top, sel_begin, sel_end, size, selected);
186         getDrawColors(di, dcolors_);
187
188         // Draw lines
189         //qDebug("x:%d", (width() - config_.Margin.left()) / config_.byteWidth());
190         const int x_count_max = (width() - config_.Margin.left()) / config_.byteWidth() + 1;
191         drawLines(painter, dcolors_, y_top, 0, x_count_max);
192
193         // Update screen buffer
194         const int draw_width  = qMin(width(), config_.maxWidth());
195         const int draw_height = count_draw_line * config_.byteHeight();
196         painter.end();
197         update(0, y_top, draw_width, draw_height);
198 }
199
200 inline void HexView::drawViewAfter(quint64 pos)
201 {
202         drawView(DRAW_AFTER, pos / HexConfig::Num - cursor_-> Top);
203 }
204
205 inline void HexView::isSelected(bool &selected, quint64 &sel_begin, quint64 &sel_end, quint64 top, int count_draw_line, uint size)
206 {
207         if (!cursor_->hasSelection()) {
208                 return;
209         }
210
211         sel_begin = qMin(cursor_->Position, cursor_->PositionAnchor);
212         sel_end   = qMax(cursor_->Position, cursor_->PositionAnchor);
213
214         if (top <= sel_end && sel_begin <= qMax(top + (HexConfig::Num * count_draw_line), top + size)) {
215                 selected = true;
216         } else {
217                 selected = false;
218         }
219 }
220
221 inline bool HexView::isSelected(quint64 pos)
222 {
223         const quint64 sel_begin = qMin(cursor_->Position, cursor_->PositionAnchor);
224         const quint64 sel_end   = qMax(cursor_->Position, cursor_->PositionAnchor);
225         return sel_begin <= pos && pos <  sel_end;
226 }
227
228 void HexView::drawLines(QPainter &painter, DCIList &dcolors, int y, int x_begin, int x_end)
229 {
230         int index_data = 0, x = 0;
231         bool reset_color = true;
232         QBrush brush;
233         QString hex;
234         hex.resize(2);
235
236         for (DCIList::iterator itr_color = dcolors.begin(); itr_color != dcolors.end(); ) {
237                 // Setup/Update color settings
238                 if (reset_color) {
239                         // Create brush for background
240                         brush = QBrush(config_.Colors[itr_color->BackgroundColor]);
241                         // Set color
242                         painter.setBackground(brush);
243                         painter.setPen(config_.Colors[itr_color->TextColor]);
244                         reset_color = false;
245                 }
246
247                 // Skip
248                 if (x < x_begin || x_end <= x) {
249                         goto COUNTUP;
250                 }
251
252                 // Draw background
253                 painter.fillRect(config_.x(x), y, config_.byteWidth(), config_.byteHeight(), brush);
254
255                 // Draw text
256                 byteToHex(buff_[index_data], hex);
257                 drawText(painter, hex, config_.x(x) + config_.ByteMargin.left(), y + config_.ByteMargin.top());
258
259 COUNTUP:// Count up
260                 index_data++;
261                 x = (x + 1) % HexConfig::Num;
262
263                 // Iterate color
264                 Q_ASSERT(0 <= itr_color->Length);
265                 if (--itr_color->Length <= 0) {
266                         // Move next color
267                         ++itr_color;
268                         // Change color
269                         reset_color = true;
270                 }
271
272                 // Move next line
273                 if (x == 0) {
274                         y += config_.byteHeight();
275                 }
276         }
277
278         // Draw empty area(after end line)
279         if (0 < x && x < x_end && x < HexConfig::Num) {
280                 //qDebug("empty: %d", x);
281                 QBrush brush(config_.Colors[Color::Background]);
282                 painter.fillRect(config_.x(x), y, width(), config_.byteHeight(), brush);
283         }
284 }
285
286 inline void HexView::drawText(QPainter &painter, const QString &hex, int x, int y)
287 {
288         painter.drawText(x, y, config_.charWidth(2), config_.charHeight(), Qt::AlignCenter, hex);
289 }
290
291 void HexView::drawCaret(bool visible)
292 {
293         drawCaret(visible, cursor_->Position);
294 }
295
296 void HexView::drawCaret(bool visible, quint64 pos)
297 {
298         // Check out of range
299         if (!(config_.top() + config_.byteHeight() < height())) {
300                 return;
301         }
302
303         // Redraw line
304         const quint64 line = cursor_->Position / HexConfig::Num;
305         if (cursor_->Top <= line && line - cursor_->Top < static_cast<unsigned int>(config_.drawableLines(height()))) {
306                 drawView(DRAW_LINE, line - cursor_->Top);
307         }
308
309         // Shape
310         const CaretShape shape = visible ? cursor_->CaretVisibleShape : cursor_->CaretInvisibleShape;
311         if (shape == CARET_NONE) {
312                 return;
313         }
314
315         // Begin paint
316         QPainter painter;
317         painter.begin(&pix_);
318         painter.setFont(config_.Font);
319
320         // Get caret coordinates
321         const int x = pos % HexConfig::Num;
322         const int y = config_.top() + config_.byteHeight() * (pos / HexConfig::Num - cursor_->Top);
323
324         // Draw shape
325         drawCaretShape(CaretDrawInfo(painter, shape, pos, x, y, pos < document_->length()));
326
327         // Finish paint and update screen buffer
328         painter.end();
329         update(config_.x(x), y, config_.byteWidth(), config_.charHeight());
330 }
331
332 void HexView::drawCaretShape(CaretDrawInfo info)
333 {
334         if (info.caret_middle) {
335                 // Copy from document
336                 uchar data;
337                 document_->get(info.pos, &data, 1);
338
339                 info.hex.resize(2);
340                 byteToHex(data, info.hex);
341         }
342
343         switch (info.shape) {
344         case CARET_LINE:
345                 drawCaretLine(info);
346                 break;
347         case CARET_BLOCK:
348                 drawCaretBlock(info);
349                 break;
350         case CARET_FRAME:
351                 drawCaretFrame(info);
352                 break;
353         case CARET_UNDERBAR:
354                 drawCaretUnderbar(info);
355                 break;
356         default:
357                 ;
358         }
359 }
360
361 void HexView::drawCaretLine(const CaretDrawInfo &info)
362 {
363         int x;
364         if (cursor_->HighNibble || !info.caret_middle) {
365                 x = config_.x(info.x);
366         } else {
367                 x = config_.x(info.x) + config_.ByteMargin.left() + config_.charWidth();
368         }
369         QBrush brush(config_.Colors[Color::CaretBackground]);
370         info.painter.fillRect(x, info.y, 2, config_.byteHeight(), brush);
371 }
372
373 void HexView::drawCaretBlock(const CaretDrawInfo &info)
374 {
375         if (info.caret_middle) {
376                 if (cursor_->HighNibble || cursor_->hasSelection()) {
377                         // Draw block byte
378                         QBrush brush(config_.Colors[Color::CaretBackground]);
379                         info.painter.setBackground(brush);
380                         info.painter.setPen(config_.Colors[Color::CaretText]);
381                         info.painter.fillRect(config_.x(info.x), info.y, config_.byteWidth(), config_.byteHeight(), brush);
382                         info.painter.drawText(config_.x(info.x) + config_.ByteMargin.left(), info.y + config_.ByteMargin.top(), config_.charWidth(2), config_.charHeight(), Qt::AlignCenter, info.hex);
383                 } else {
384                         // Draw block lowwer nibble
385                         QBrush brush(config_.Colors[Color::CaretBackground]);
386                         info.painter.setBackground(brush);
387                         info.painter.setPen(config_.Colors[Color::CaretText]);
388                         info.painter.fillRect(config_.x(info.x) + config_.ByteMargin.left() + config_.charWidth(), info.y, config_.charWidth() + config_.ByteMargin.right(), config_.byteHeight(), brush);
389                         QString low(info.hex[1]);
390                         info.painter.drawText(config_.x(info.x) + config_.ByteMargin.left() + config_.charWidth(), info.y + config_.ByteMargin.top(), config_.charWidth(2), config_.charHeight(), Qt::AlignLeft, low);
391                 }
392         } else {
393                 // Draw block without data
394                 QBrush brush(config_.Colors[Color::CaretBackground]);
395                 info.painter.fillRect(config_.x(info.x), info.y, config_.byteWidth(), config_.byteHeight(), brush);
396         }
397 }
398
399 void HexView::drawCaretFrame(const CaretDrawInfo &info)
400 {
401         int width, x;
402         if (cursor_->HighNibble || !info.caret_middle) {
403                 width = config_.byteWidth() - 1;
404                 x = config_.x(info.x);
405         } else {
406                 width = config_.charWidth() + config_.ByteMargin.right() - 1;
407                 x = config_.x(info.x) + config_.charWidth() + config_.ByteMargin.left();
408         }
409         info.painter.setPen(config_.Colors[Color::CaretBackground]);
410         info.painter.drawRect(x, info.y, width, config_.byteHeight() - 1);
411 }
412
413 void HexView::drawCaretUnderbar(const CaretDrawInfo &info)
414 {
415         int width, x;
416         if (cursor_->HighNibble || !info.caret_middle) {
417                 width = config_.byteWidth() - 1;
418                 x = config_.x(info.x);
419         } else {
420                 width = config_.charWidth() + config_.ByteMargin.right() - 1;
421                 x = config_.x(info.x) + config_.ByteMargin.left() + config_.charWidth();
422         }
423
424         QBrush brush(config_.Colors[Color::CaretBackground]);
425         info.painter.fillRect(x, info.y + config_.byteHeight() - 2, width, 2, brush);
426 }
427
428 void HexView::byteToHex(uchar c, QString &h)
429 {
430         const uchar H = (c >> 4) & 0xF;
431         if (H <= 9) {
432                 h[0] = QChar('0' + H);
433         } else {
434                 h[0] = QChar('A' + H - 10);
435         }
436         const uchar L = c & 0xF;
437         if (L <= 9) {
438                 h[1] = QChar('0' + L);
439         } else {
440                 h[1] = QChar('A' + L - 10);
441         }
442 }
443
444 void HexView::mousePressEvent(QMouseEvent *ev)
445 {
446         if (ev->button() == Qt::LeftButton) {
447
448                 cursor_->HighNibble = true;
449                 cursor_->movePosition(posAt(ev->pos()), false, false);
450
451                 // Start mouse capture
452                 grabMouse();
453         }
454 }
455
456 void HexView::mouseMoveEvent(QMouseEvent *ev)
457 {
458         if (ev->button() == Qt::LeftButton) {
459                 // FIXME: move up/down automatically
460                 if (height() < ev->pos().y()) {
461                         return;
462                 }
463                 cursor_->movePosition(posAt(ev->pos()), true, false);
464         }
465 }
466
467 void HexView::mouseReleaseEvent(QMouseEvent *)
468 {
469         //qDebug("mouse release");
470
471         // End mouse capture
472         releaseMouse();
473 }
474
475 quint64 HexView::posAt(const QPoint &pos)
476 {
477         int x = config_.XToPos(pos.x());
478         int y = config_.YToLine(pos.y());
479
480         if (x < 0) {
481                 x = 0;
482         }
483         if (y < 0) {
484                 x = y = 0;
485         }
486
487         return qMin((cursor_->Top + y) * HexConfig::Num + x, document_->length());
488 }
489
490 // Enable caret blink
491 void HexView::setCaretBlink(bool enable)
492 {
493         if (!config_.EnableCaret || !config_.CaretBlinkTime) {
494                 return;
495         }
496         if (enable) {
497                 if (cursor_->CaretTimerId == 0) {
498                         cursor_->CaretTimerId = startTimer(config_.CaretBlinkTime);
499                 }
500         } else {
501                 if (cursor_->CaretTimerId != 0) {
502                         killTimer(cursor_->CaretTimerId);
503                         cursor_->CaretTimerId = 0;
504                 }
505         }
506 }
507
508 void HexView::timerEvent(QTimerEvent *ev)
509 {
510         if (cursor_->CaretTimerId == ev->timerId()) {
511                 // Caret blink
512                 drawCaret(cursor_->HexCaretVisible);
513                 cursor_->turnHexCaretVisible();
514         }
515 }
516
517 void HexView::keyPressEvent(QKeyEvent *ev)
518 {
519         if (ev == QKeySequence::SelectAll) {
520                 //ev->accept();
521                 //all
522                 return;
523         } else if (ev == QKeySequence::Undo) {
524                 return;
525         } else if (ev == QKeySequence::Redo) {
526                 return;
527         }
528
529
530         // TODO: support keyboard remap
531
532         bool keepAnchor = ev->modifiers() & Qt::SHIFT ? true : false;
533         switch (ev->key()) {
534         case Qt::Key_Home:
535                 cursor_->HighNibble = true;
536                 cursor_->movePosition(0, keepAnchor, false);
537                 break;
538         case Qt::Key_End:
539                 cursor_->HighNibble = true;
540                 cursor_->movePosition(document_->length(), keepAnchor, false);
541                 break;
542         case Qt::Key_Left:
543                 cursor_->HighNibble = true;
544                 cursor_->moveRelativePosition(-1, keepAnchor, false);
545                 break;
546         case Qt::Key_Right:
547                 cursor_->HighNibble = true;
548                 cursor_->moveRelativePosition(1, keepAnchor, false);
549                 break;
550         case Qt::Key_Up:
551                 cursor_->HighNibble = true;
552                 cursor_->moveRelativePosition(-16, keepAnchor, false);
553                 break;
554         case Qt::Key_Down:
555                 cursor_->HighNibble = true;
556                 cursor_->moveRelativePosition(16, keepAnchor, false);
557                 break;
558         case Qt::Key_PageUp:
559                 cursor_->HighNibble = true;
560                 cursor_->moveRelativePosition(-16 * 15, keepAnchor, true);
561                 break;
562         case Qt::Key_PageDown:
563                 cursor_->HighNibble = true;
564                 cursor_->moveRelativePosition(16 * 15, keepAnchor, true);
565                 break;
566         case Qt::Key_Backspace:
567                 if (cursor_->hasSelection()) {
568                         const quint64 pos = qMin(cursor_->Position, cursor_->PositionAnchor);
569                         const quint64 len = qMax(cursor_->Position, cursor_->PositionAnchor) - pos;
570                         removeData(pos, len);
571                         cursor_->moveRelativePosition(0, false, false);
572                         cursor_->HighNibble = true;
573                 } else if (0 < cursor_->Position) {
574                         removeData(cursor_->Position - 1, 1);
575                         cursor_->moveRelativePosition(-1, false, false);
576                         cursor_->HighNibble = true;
577                 }
578                 break;
579         case Qt::Key_Insert:
580                 qDebug("key insert");
581                 cursor_->reverseInsert();
582                 break;
583         case Qt::Key_Delete:
584                 if (cursor_->hasSelection()) {
585                         const quint64 pos = qMin(cursor_->Position, cursor_->PositionAnchor);
586                         const quint64 len = qMax(cursor_->Position, cursor_->PositionAnchor) - pos;
587                         removeData(pos, len);
588                         cursor_->moveRelativePosition(0, false, false);
589                         cursor_->HighNibble = true;
590                 } else if (cursor_->Position < document_->length()) {
591                         removeData(cursor_->Position, 1);
592                         cursor_->moveRelativePosition(0, false, false);
593                         cursor_->HighNibble = true;
594                 }
595                 break;
596         default:
597                 {
598                         // copy from QtCreator
599                         QString text = ev->text();
600                         for (int i = 0; i < text.length(); i++) {
601                                 QChar c = text.at(i).toLower();
602                                 int nibble = -1;
603                                 if (c.unicode() >= 'a' && c.unicode() <= 'f') {
604                                         nibble = c.unicode() - 'a' + 10;
605                                 } else if (c.unicode() >= '0' && c.unicode() <= '9') {
606                                         nibble = c.unicode() - '0';
607                                 }
608                                 if (nibble < 0) {
609                                         continue;
610                                 }
611                                 if (cursor_->Insert && cursor_->HighNibble) {
612                                 //if (false) {
613                                         // Inserte mode
614                                         quint64 pos = qMin(cursor_->Position, cursor_->PositionAnchor);
615
616                                         // Replace data if selected
617                                         if (cursor_->hasSelection()) {
618                                                 // TODO: Support Undo
619                                                 // Off redrawing temporary for redrawing on insertion
620                                                 document_->remove(pos, qMax(cursor_->Position, cursor_->PositionAnchor) - pos);
621                                                 cursor_->Position = pos;
622                                                 cursor_->resetAnchor();
623                                                 // TODO: remove and refresh collectly
624                                                 cursor_->moveRelativePosition(0, false, false);
625                                         }
626
627                                         insertData(pos, nibble << 4);
628                                         cursor_->HighNibble = false;
629                                 } else if (cursor_->Position < document_->length()) {
630                                         // Ovewrite mode
631                                         uchar currentCharacter;
632                                         document_->get(cursor_->Position, &currentCharacter, 1);
633                                         if (cursor_->HighNibble) {
634                                                 changeData(cursor_->Position, (nibble << 4) + (currentCharacter & 0x0f), true);
635                                                 cursor_->HighNibble = false;
636                                                 // TODO: fix Clear and redraw caret, implment Event
637                                                 drawView(DRAW_LINE, cursor_->Position / HexConfig::Num - cursor_->Top);
638                                                 drawCaret();
639                                         } else {
640                                                 changeData(cursor_->Position, nibble + (currentCharacter & 0xf0));
641                                                 cursor_->moveRelativePosition(1, false, false);
642                                         }
643                                 } else {
644                                         break;
645                                 }
646                         }
647                 }
648                 return;
649         }
650 }
651
652 void HexView::changeData(quint64 pos, uchar character, bool highNibble)
653 {
654         document_->remove(pos, 1);
655         document_->insert(pos, &character, 1);
656         cursor_->HighNibble = !highNibble;
657         // TODO: implement Redraw Event
658 }
659
660 void HexView::insertData(quint64 pos, uchar character)
661 {
662         document_->insert(pos, &character, 1);
663         // TODO: implement Redraw Event
664         drawViewAfter(pos);
665         drawCaret();
666 }
667
668 void HexView::removeData(quint64 pos, quint64 len)
669 {
670         document_->remove(pos, len);
671         // TODO: implement Redraw Event
672         drawViewAfter(pos);
673 }
674
675
676
677
678 }       // namespace