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::isSelected(bool &selected, quint64 &sel_begin, quint64 &sel_end, quint64 top, int count_draw_line, uint size)
202 if (!cursor_->hasSelection()) {
206 sel_begin = qMin(cursor_->Position, cursor_->PositionAnchor);
207 sel_end = qMax(cursor_->Position, cursor_->PositionAnchor);
209 if (top <= sel_end && sel_begin <= qMax(top + (HexConfig::Num * count_draw_line), top + size)) {
216 inline bool HexView::isSelected(quint64 pos)
218 const quint64 sel_begin = qMin(cursor_->Position, cursor_->PositionAnchor);
219 const quint64 sel_end = qMax(cursor_->Position, cursor_->PositionAnchor);
220 return sel_begin <= pos && pos < sel_end;
223 void HexView::drawLines(QPainter &painter, DCIList &dcolors, int y, int x_begin, int x_end)
225 int index_data = 0, x = 0;
226 bool reset_color = true;
231 for (DCIList::iterator itr_color = dcolors.begin(); itr_color != dcolors.end(); ) {
232 // Setup/Update color settings
234 // Create brush for background
235 brush = QBrush(config_.Colors[itr_color->BackgroundColor]);
237 painter.setBackground(brush);
238 painter.setPen(config_.Colors[itr_color->TextColor]);
243 if (x < x_begin || x_end <= x) {
248 painter.fillRect(config_.x(x), y, config_.byteWidth(), config_.byteHeight(), brush);
251 byteToHex(buff_[index_data], hex);
252 drawText(painter, hex, config_.x(x) + config_.ByteMargin.left(), y + config_.ByteMargin.top());
256 x = (x + 1) % HexConfig::Num;
259 Q_ASSERT(0 <= itr_color->Length);
260 if (--itr_color->Length <= 0) {
269 y += config_.byteHeight();
273 // Draw empty area(after end line)
274 if (0 < x && x < x_end && x < HexConfig::Num) {
275 //qDebug("empty: %d", x);
276 QBrush brush(config_.Colors[Color::Background]);
277 painter.fillRect(config_.x(x), y, width(), config_.byteHeight(), brush);
281 inline void HexView::drawText(QPainter &painter, const QString &hex, int x, int y)
283 painter.drawText(x, y, config_.charWidth(2), config_.charHeight(), Qt::AlignCenter, hex);
286 void HexView::drawCaret(bool visible)
288 drawCaret(visible, cursor_->Position);
291 void HexView::drawCaret(bool visible, quint64 pos)
293 // Check out of range
294 if (!(config_.top() + config_.byteHeight() < height())) {
299 const quint64 line = cursor_->Position / HexConfig::Num;
300 if (cursor_->Top <= line && line - cursor_->Top < static_cast<unsigned int>(config_.drawableLines(height()))) {
301 drawView(DRAW_LINE, line - cursor_->Top);
305 const CaretShape shape = visible ? cursor_->CaretVisibleShape : cursor_->CaretInvisibleShape;
306 if (shape == CARET_NONE) {
312 painter.begin(&pix_);
313 painter.setFont(config_.Font);
315 // Get caret coordinates
316 const int x = pos % HexConfig::Num;
317 const int y = config_.top() + config_.byteHeight() * (pos / HexConfig::Num - cursor_->Top);
320 drawCaretShape(CaretDrawInfo(painter, shape, pos, x, y, pos < document_->length()));
322 // Finish paint and update screen buffer
324 update(config_.x(x), y, config_.byteWidth(), config_.charHeight());
327 void HexView::drawCaretShape(CaretDrawInfo info)
329 if (info.caret_middle) {
330 // Copy from document
332 document_->get(info.pos, &data, 1);
335 byteToHex(data, info.hex);
338 switch (info.shape) {
343 drawCaretBlock(info);
346 drawCaretFrame(info);
349 drawCaretUnderbar(info);
356 void HexView::drawCaretLine(const CaretDrawInfo &info)
359 if (cursor_->HighNibble || !info.caret_middle) {
360 x = config_.x(info.x);
362 x = config_.x(info.x) + config_.ByteMargin.left() + config_.charWidth();
364 QBrush brush(config_.Colors[Color::CaretBackground]);
365 info.painter.fillRect(x, info.y, 2, config_.byteHeight(), brush);
368 void HexView::drawCaretBlock(const CaretDrawInfo &info)
370 if (info.caret_middle) {
371 if (cursor_->HighNibble || cursor_->hasSelection()) {
373 QBrush brush(config_.Colors[Color::CaretBackground]);
374 info.painter.setBackground(brush);
375 info.painter.setPen(config_.Colors[Color::CaretText]);
376 info.painter.fillRect(config_.x(info.x), info.y, config_.byteWidth(), config_.byteHeight(), brush);
377 info.painter.drawText(config_.x(info.x) + config_.ByteMargin.left(), info.y + config_.ByteMargin.top(), config_.charWidth(2), config_.charHeight(), Qt::AlignCenter, info.hex);
379 // Draw block lowwer nibble
380 QBrush brush(config_.Colors[Color::CaretBackground]);
381 info.painter.setBackground(brush);
382 info.painter.setPen(config_.Colors[Color::CaretText]);
383 info.painter.fillRect(config_.x(info.x) + config_.ByteMargin.left() + config_.charWidth(), info.y, config_.charWidth() + config_.ByteMargin.right(), config_.byteHeight(), brush);
384 QString low(info.hex[1]);
385 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);
388 // Draw block without data
389 QBrush brush(config_.Colors[Color::CaretBackground]);
390 info.painter.fillRect(config_.x(info.x), info.y, config_.byteWidth(), config_.byteHeight(), brush);
394 void HexView::drawCaretFrame(const CaretDrawInfo &info)
397 if (cursor_->HighNibble || !info.caret_middle) {
398 width = config_.byteWidth() - 1;
399 x = config_.x(info.x);
401 width = config_.charWidth() + config_.ByteMargin.right() - 1;
402 x = config_.x(info.x) + config_.charWidth() + config_.ByteMargin.left();
404 info.painter.setPen(config_.Colors[Color::CaretBackground]);
405 info.painter.drawRect(x, info.y, width, config_.byteHeight() - 1);
408 void HexView::drawCaretUnderbar(const CaretDrawInfo &info)
411 if (cursor_->HighNibble || !info.caret_middle) {
412 width = config_.byteWidth() - 1;
413 x = config_.x(info.x);
415 width = config_.charWidth() + config_.ByteMargin.right() - 1;
416 x = config_.x(info.x) + config_.ByteMargin.left() + config_.charWidth();
419 QBrush brush(config_.Colors[Color::CaretBackground]);
420 info.painter.fillRect(x, info.y + config_.byteHeight() - 2, width, 2, brush);
423 void HexView::byteToHex(uchar c, QString &h)
425 const uchar H = (c >> 4) & 0xF;
427 h[0] = QChar('0' + H);
429 h[0] = QChar('A' + H - 10);
431 const uchar L = c & 0xF;
433 h[1] = QChar('0' + L);
435 h[1] = QChar('A' + L - 10);
439 void HexView::mousePressEvent(QMouseEvent *ev)
441 if (ev->button() == Qt::LeftButton) {
443 cursor_->HighNibble = true;
444 cursor_->movePosition(posAt(ev->pos()), false, false);
446 // Start mouse capture
451 void HexView::mouseMoveEvent(QMouseEvent *ev)
453 if (ev->button() == Qt::LeftButton) {
454 // FIXME: move down automatically
455 if (height() < ev->pos().y()) {
458 cursor_->movePosition(posAt(ev->pos()), true, false);
462 void HexView::mouseReleaseEvent(QMouseEvent *)
464 //qDebug("mouse release");
470 quint64 HexView::posAt(const QPoint &pos)
472 int x = config_.XToPos(pos.x());
473 int y = config_.YToLine(pos.y());
482 return qMin((cursor_->Top + y) * HexConfig::Num + x, document_->length());
485 // Enable caret blink
486 void HexView::setCaretBlink(bool enable)
488 if (!config_.EnableCaret || !config_.CaretBlinkTime) {
492 if (cursor_->CaretTimerId == 0) {
493 cursor_->CaretTimerId = startTimer(config_.CaretBlinkTime);
496 if (cursor_->CaretTimerId != 0) {
497 killTimer(cursor_->CaretTimerId);
498 cursor_->CaretTimerId = 0;
503 void HexView::timerEvent(QTimerEvent *ev)
505 if (cursor_->CaretTimerId == ev->timerId()) {
507 drawCaret(cursor_->HexCaretVisible);
508 cursor_->turnHexCaretVisible();
512 void HexView::keyPressEvent(QKeyEvent *ev)
514 if (ev == QKeySequence::SelectAll) {
518 } else if (ev == QKeySequence::Undo) {
520 } else if (ev == QKeySequence::Redo) {
525 // TODO: support keyboard remap
527 bool keepAnchor = ev->modifiers() & Qt::SHIFT ? true : false;
530 cursor_->HighNibble = true;
531 cursor_->movePosition(0, keepAnchor, false);
534 cursor_->HighNibble = true;
535 cursor_->movePosition(document_->length(), keepAnchor, false);
538 cursor_->HighNibble = true;
539 cursor_->moveRelativePosition(-1, keepAnchor, false);
542 cursor_->HighNibble = true;
543 cursor_->moveRelativePosition(1, keepAnchor, false);
546 cursor_->HighNibble = true;
547 cursor_->moveRelativePosition(-16, keepAnchor, false);
550 cursor_->HighNibble = true;
551 cursor_->moveRelativePosition(16, keepAnchor, false);
554 cursor_->HighNibble = true;
555 cursor_->moveRelativePosition(-16 * 15, keepAnchor, true);
557 case Qt::Key_PageDown:
558 cursor_->HighNibble = true;
559 cursor_->moveRelativePosition(16 * 15, keepAnchor, true);
561 case Qt::Key_Backspace:
562 qDebug("key backspace");
565 qDebug("key insert");
566 cursor_->Insert = !cursor_->Insert;
569 qDebug("key delete");
573 // copy from QtCreator
574 QString text = ev->text();
575 for (int i = 0; i < text.length(); i++) {
576 QChar c = text.at(i).toLower();
578 if (c.unicode() >= 'a' && c.unicode() <= 'f') {
579 nibble = c.unicode() - 'a' + 10;
580 } else if (c.unicode() >= '0' && c.unicode() <= '9') {
581 nibble = c.unicode() - '0';
586 if (cursor_->HighNibble) {
587 // TODO: insert/rewrite document
588 //changeData(m_cursorPosition, (nibble << 4) + (m_data[m_cursorPosition] & 0x0f), true);
589 cursor_->HighNibble = false;
590 // Clear and redraw caret
591 drawView(DRAW_LINE, cursor_->Position / HexConfig::Num - cursor_->Top);
594 // TODO: insert/rewrite document
595 //changeData(m_cursorPosition, nibble + (m_data[m_cursorPosition] & 0xf0));
596 cursor_->HighNibble = true;
597 cursor_->moveRelativePosition(1, false, false);