7 #include "../document.h"
8 #include "../highlight.h"
14 ////////////////////////////////////////
16 HexConfig::HexConfig()
18 , ByteMargin(3, 0, 2, 0)
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);
33 Font.setFixedPitch(true);
38 void HexConfig::update()
40 // TODO: set ByteMargin value(left=charWidth/2, right=charWidth/2)
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();
49 for (int i = 0; i < Num; i++) {
50 x_end[i] = x_begin[i] + charWidth(2) + ByteMargin.right();
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();
58 x_area[Num] = x_area[Num-1] + byteWidth();
61 int HexConfig::drawableLines(int height) const
63 const int y = top() + byteMargin().top();
64 return (height - y + byteHeight()) / byteHeight();
67 int HexConfig::XToPos(int x) const
69 if (x < Margin.left()) {
73 return (int)distance(x_area, lower_bound(x_area, x_area + Num + 1, x)) - 1;
76 int HexConfig::YToLine(int y) const
81 return (y - top()) / byteHeight();
84 ////////////////////////////////////////
87 HexView::HexView(QWidget *parent, Document *doc, Highlight *hi)
88 : ::View(parent, doc, hi)
89 , cursor_(new Cursor(doc, this))
91 // Enable keyboard input
92 setFocusPolicy(Qt::WheelFocus);
95 void HexView::resizeEvent(QResizeEvent *rs)
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]);
104 void HexView::drawView(DrawMode mode, int line_start, int end)
106 //qDebug("refresh event mode:%d line:%d end:%d", mode, line_start, end);
107 //qDebug(" pos:%llu, anchor:%llu", cursor_->Position, cursor_->PositionAnchor);
109 // FIXME: refactoring refresh event
111 painter.begin(&pix_);
112 painter.setFont(config_.Font);
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);
119 // Update screen buffer
120 update(0, 0, width(), height());
124 Q_ASSERT(0 <= line_start);
125 Q_ASSERT(static_cast<uint>(line_start) <= document_->length() / HexConfig::Num + 1);
127 Q_ASSERT(static_cast<uint>(end) <= document_->length() / HexConfig::Num + 1);
130 int y_top = config_.top();
131 int y = config_.top() + config_.byteMargin().top();
132 int count_draw_line, max_y;
134 // Get minumum drawing area
137 count_draw_line = config_.drawableLines(height());
140 y_top += config_.byteHeight() * line_start;
141 y += config_.byteHeight() * line_start;
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);
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);
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);
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);
173 // Copy from document
174 if (buff_.capacity() < size) {
177 document_->get(top, &buff_[0], size);
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);
184 // TODO: Adding cache class for color highligh data
185 ::DrawInfo di(y, top, sel_begin, sel_end, size, selected);
186 getDrawColors(di, dcolors_);
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);
193 // Update screen buffer
194 const int draw_width = qMin(width(), config_.maxWidth());
195 const int draw_height = count_draw_line * config_.byteHeight();
197 update(0, y_top, draw_width, draw_height);
200 inline void HexView::drawViewAfter(quint64 pos)
202 drawView(DRAW_AFTER, pos / HexConfig::Num - cursor_-> Top);
205 inline void HexView::isSelected(bool &selected, quint64 &sel_begin, quint64 &sel_end, quint64 top, int count_draw_line, uint size)
207 if (!cursor_->hasSelection()) {
211 sel_begin = qMin(cursor_->Position, cursor_->PositionAnchor);
212 sel_end = qMax(cursor_->Position, cursor_->PositionAnchor);
214 if (top <= sel_end && sel_begin <= qMax(top + (HexConfig::Num * count_draw_line), top + size)) {
221 inline bool HexView::isSelected(quint64 pos)
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;
228 void HexView::drawLines(QPainter &painter, DCIList &dcolors, int y, int x_begin, int x_end)
230 int index_data = 0, x = 0;
231 bool reset_color = true;
236 for (DCIList::iterator itr_color = dcolors.begin(); itr_color != dcolors.end(); ) {
237 // Setup/Update color settings
239 // Create brush for background
240 brush = QBrush(config_.Colors[itr_color->BackgroundColor]);
242 painter.setBackground(brush);
243 painter.setPen(config_.Colors[itr_color->TextColor]);
248 if (x < x_begin || x_end <= x) {
253 painter.fillRect(config_.x(x), y, config_.byteWidth(), config_.byteHeight(), brush);
256 byteToHex(buff_[index_data], hex);
257 drawText(painter, hex, config_.x(x) + config_.ByteMargin.left(), y + config_.ByteMargin.top());
261 x = (x + 1) % HexConfig::Num;
264 Q_ASSERT(0 <= itr_color->Length);
265 if (--itr_color->Length <= 0) {
274 y += config_.byteHeight();
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);
286 inline void HexView::drawText(QPainter &painter, const QString &hex, int x, int y)
288 painter.drawText(x, y, config_.charWidth(2), config_.charHeight(), Qt::AlignCenter, hex);
291 void HexView::drawCaret(bool visible)
293 drawCaret(visible, cursor_->Position);
296 void HexView::drawCaret(bool visible, quint64 pos)
298 // Check out of range
299 if (!(config_.top() + config_.byteHeight() < height())) {
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);
310 const CaretShape shape = visible ? cursor_->CaretVisibleShape : cursor_->CaretInvisibleShape;
311 if (shape == CARET_NONE) {
317 painter.begin(&pix_);
318 painter.setFont(config_.Font);
320 // Get caret coordinates
321 const int x = pos % HexConfig::Num;
322 const int y = config_.top() + config_.byteHeight() * (pos / HexConfig::Num - cursor_->Top);
325 drawCaretShape(CaretDrawInfo(painter, shape, pos, x, y, pos < document_->length()));
327 // Finish paint and update screen buffer
329 update(config_.x(x), y, config_.byteWidth(), config_.charHeight());
332 void HexView::drawCaretShape(CaretDrawInfo info)
334 if (info.caret_middle) {
335 // Copy from document
337 document_->get(info.pos, &data, 1);
340 byteToHex(data, info.hex);
343 switch (info.shape) {
348 drawCaretBlock(info);
351 drawCaretFrame(info);
354 drawCaretUnderbar(info);
361 void HexView::drawCaretLine(const CaretDrawInfo &info)
364 if (cursor_->HighNibble || !info.caret_middle) {
365 x = config_.x(info.x);
367 x = config_.x(info.x) + config_.ByteMargin.left() + config_.charWidth();
369 QBrush brush(config_.Colors[Color::CaretBackground]);
370 info.painter.fillRect(x, info.y, 2, config_.byteHeight(), brush);
373 void HexView::drawCaretBlock(const CaretDrawInfo &info)
375 if (info.caret_middle) {
376 if (cursor_->HighNibble || cursor_->hasSelection()) {
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);
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);
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);
399 void HexView::drawCaretFrame(const CaretDrawInfo &info)
402 if (cursor_->HighNibble || !info.caret_middle) {
403 width = config_.byteWidth() - 1;
404 x = config_.x(info.x);
406 width = config_.charWidth() + config_.ByteMargin.right() - 1;
407 x = config_.x(info.x) + config_.charWidth() + config_.ByteMargin.left();
409 info.painter.setPen(config_.Colors[Color::CaretBackground]);
410 info.painter.drawRect(x, info.y, width, config_.byteHeight() - 1);
413 void HexView::drawCaretUnderbar(const CaretDrawInfo &info)
416 if (cursor_->HighNibble || !info.caret_middle) {
417 width = config_.byteWidth() - 1;
418 x = config_.x(info.x);
420 width = config_.charWidth() + config_.ByteMargin.right() - 1;
421 x = config_.x(info.x) + config_.ByteMargin.left() + config_.charWidth();
424 QBrush brush(config_.Colors[Color::CaretBackground]);
425 info.painter.fillRect(x, info.y + config_.byteHeight() - 2, width, 2, brush);
428 void HexView::byteToHex(uchar c, QString &h)
430 const uchar H = (c >> 4) & 0xF;
432 h[0] = QChar('0' + H);
434 h[0] = QChar('A' + H - 10);
436 const uchar L = c & 0xF;
438 h[1] = QChar('0' + L);
440 h[1] = QChar('A' + L - 10);
444 void HexView::mousePressEvent(QMouseEvent *ev)
446 if (ev->button() == Qt::LeftButton) {
448 cursor_->HighNibble = true;
449 cursor_->movePosition(posAt(ev->pos()), false, false);
451 // Start mouse capture
456 void HexView::mouseMoveEvent(QMouseEvent *ev)
458 if (ev->button() == Qt::LeftButton) {
459 // FIXME: move up/down automatically
460 if (height() < ev->pos().y()) {
463 cursor_->movePosition(posAt(ev->pos()), true, false);
467 void HexView::mouseReleaseEvent(QMouseEvent *)
469 //qDebug("mouse release");
475 quint64 HexView::posAt(const QPoint &pos)
477 int x = config_.XToPos(pos.x());
478 int y = config_.YToLine(pos.y());
487 return qMin((cursor_->Top + y) * HexConfig::Num + x, document_->length());
490 // Enable caret blink
491 void HexView::setCaretBlink(bool enable)
493 if (!config_.EnableCaret || !config_.CaretBlinkTime) {
497 if (cursor_->CaretTimerId == 0) {
498 cursor_->CaretTimerId = startTimer(config_.CaretBlinkTime);
501 if (cursor_->CaretTimerId != 0) {
502 killTimer(cursor_->CaretTimerId);
503 cursor_->CaretTimerId = 0;
508 void HexView::timerEvent(QTimerEvent *ev)
510 if (cursor_->CaretTimerId == ev->timerId()) {
512 drawCaret(cursor_->HexCaretVisible);
513 cursor_->turnHexCaretVisible();
517 void HexView::keyPressEvent(QKeyEvent *ev)
519 if (ev == QKeySequence::SelectAll) {
523 } else if (ev == QKeySequence::Undo) {
525 } else if (ev == QKeySequence::Redo) {
530 // TODO: support keyboard remap
532 bool keepAnchor = ev->modifiers() & Qt::SHIFT ? true : false;
535 cursor_->HighNibble = true;
536 cursor_->movePosition(0, keepAnchor, false);
539 cursor_->HighNibble = true;
540 cursor_->movePosition(document_->length(), keepAnchor, false);
543 cursor_->HighNibble = true;
544 cursor_->moveRelativePosition(-1, keepAnchor, false);
547 cursor_->HighNibble = true;
548 cursor_->moveRelativePosition(1, keepAnchor, false);
551 cursor_->HighNibble = true;
552 cursor_->moveRelativePosition(-16, keepAnchor, false);
555 cursor_->HighNibble = true;
556 cursor_->moveRelativePosition(16, keepAnchor, false);
559 cursor_->HighNibble = true;
560 cursor_->moveRelativePosition(-16 * 15, keepAnchor, true);
562 case Qt::Key_PageDown:
563 cursor_->HighNibble = true;
564 cursor_->moveRelativePosition(16 * 15, keepAnchor, true);
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;
580 qDebug("key insert");
581 cursor_->reverseInsert();
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;
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();
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';
611 if (cursor_->Insert && cursor_->HighNibble) {
614 quint64 pos = qMin(cursor_->Position, cursor_->PositionAnchor);
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);
627 insertData(pos, nibble << 4);
628 cursor_->HighNibble = false;
629 } else if (cursor_->Position < document_->length()) {
631 uchar currentCharacter;
632 document_->get(cursor_->Position, ¤tCharacter, 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);
640 changeData(cursor_->Position, nibble + (currentCharacter & 0xf0));
641 cursor_->moveRelativePosition(1, false, false);
652 void HexView::changeData(quint64 pos, uchar character, bool highNibble)
654 document_->remove(pos, 1);
655 document_->insert(pos, &character, 1);
656 cursor_->HighNibble = !highNibble;
657 // TODO: implement Redraw Event
660 void HexView::insertData(quint64 pos, uchar character)
662 document_->insert(pos, &character, 1);
663 // TODO: implement Redraw Event
668 void HexView::removeData(quint64 pos, quint64 len)
670 document_->remove(pos, len);
671 // TODO: implement Redraw Event