7 #include "../document.h"
8 #include "../highlight.h"
14 ////////////////////////////////////////
16 HexConfig::HexConfig()
18 , ByteMargin(2, 0, 2, 0)
25 Colors[Color::Background] = QColor(0xCE,0xFF,0xCE);
26 Colors[Color::Text] = QColor(0,0,0);
27 Colors[Color::SelBackground] = QColor(0xCC,0xCC,0xFF);
28 Colors[Color::SelText] = QColor(0,0x40,0x40);
29 Colors[Color::CaretBackground] = QColor(0xFF, 0, 0);
30 Colors[Color::CaretText] = QColor(0xFA,0xFA,0x20);
33 Font.setFixedPitch(true);
38 void HexConfig::calculate()
41 x_[0] = Margin.left() + ByteMargin.left();
42 for (int i = 1; i < Num; i++) {
43 x_[i] = x_[i-1] + byteWidth();
47 for (int i = 0; i < Num; i++) {
48 X_[i] = x_[i] + charWidth(2) + ByteMargin.right();
52 xarea_[0] = Margin.left() + ByteMargin.left();
53 for (int i = 1; i < Num; i++) {
54 xarea_[i] = xarea_[i-1] + byteWidth();
56 xarea_[Num] = xarea_[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(xarea_, lower_bound(xarea_, xarea_ + Num + 2, x)) - 1;
76 int HexConfig::YToLine(int y) const
78 if (y < Margin.top()) {
81 return (y - Margin.top()) / byteHeight();
84 ////////////////////////////////////////
91 DRAW_RANGE, // [begin, end)
94 HexView::HexView(QWidget *parent, Document *doc, Highlight *hi)
95 : ::View(parent, doc, hi)
96 , cursor(new Cursor(doc, this))
98 // Enable keyboard input
99 setFocusPolicy(Qt::WheelFocus);
102 void HexView::resizeEvent(QResizeEvent *rs)
104 QSize size(min(rs->size().width(), config.maxWidth()), rs->size().height());
105 QResizeEvent resize(size, rs->oldSize());
106 View::resizeEvent(&resize);
107 pix_.fill(config.Colors[Color::Background]);
111 void HexView::drawView()
116 void HexView::drawView(int type, int line_start, int end)
118 qDebug("refresh event type:%d line:%d end:%d", type, line_start, end);
119 qDebug(" end:%llu endOld:%llu pos:%llu", cursor->SelEnd, cursor->SelEndOld, cursor->Position);
121 // FIXME: refactoring refresh event
123 painter.begin(&pix_);
124 painter.setFont(config.Font);
126 if (!document->length()) {
127 // TODO: draw Empty Background only
128 QBrush brush(config.Colors[Color::Background]);
129 painter.fillRect(0, 0, width(), height(), brush);
131 // Update screen buffer
132 update(0, 0, width(), height());
136 Q_ASSERT(0 <= line_start && line_start <= document->length() / HexConfig::Num + 1);
137 Q_ASSERT(0 <= end && end <= document->length() / HexConfig::Num + 1);
140 int y_top = config.top();
141 int y = config.top() + config.byteMargin().top();
142 int count_line, max_y;
144 // Get minumum drawing area
147 count_line = config.drawableLines(height());
150 y_top += config.byteHeight() * line_start;
151 y += config.byteHeight() * line_start;
155 y_top += config.byteHeight() * line_start;
156 y += config.byteHeight() * line_start;
157 max_y = max(y + config.byteHeight(), height());
158 count_line = config.drawableLines(max_y - y);
161 y_top += config.byteHeight() * line_start;
162 y += config.byteHeight() * line_start;
163 max_y = min(y + config.byteHeight() * end, height());
164 count_line = config.drawableLines(max_y - y);
168 // Get top position of view
169 const quint64 top = (cursor->Top + line_start) * HexConfig::Num;
170 const uint size = min(document->length() - top, (quint64)HexConfig::Num * count_line);
172 // Draw empty area(after end line)
173 if (type == DRAW_ALL || type == DRAW_AFTER) {
174 QBrush brush(config.Colors[Color::Background]);
175 const int y_start = y_top + max(0, count_line - 1) * config.byteHeight();
176 painter.fillRect(0, y_start, width(), height(), brush);
179 // Copy from document
180 if (buff_.capacity() < size) {
183 document->get(top, &buff_[0], size);
185 // Get selectead area
186 bool selected = false;
187 quint64 sel_begin = 0, sel_end = 0;
188 isSelected(selected, sel_begin, sel_end, top, count_line, size);
190 // TODO: Adding cache class for color highligh data
191 ::DrawInfo di(y, top, sel_begin, sel_end, size, selected);
192 getDrawColors(di, dcolors_);
195 qDebug("x:%d", (width() - config.Margin.left()) / config.byteWidth());
196 const int x_count_max = (width() - config.Margin.left()) / config.byteWidth() + 1;
197 drawLines(painter, dcolors_, y_top, 0, x_count_max);
200 // Update screen buffer
201 const int draw_width = min(width(), config.maxWidth());
202 const int draw_height = count_line * config.byteHeight();
203 update(0, y_top, draw_width, draw_height);
206 inline void HexView::isSelected(bool &selected, quint64 &sel_begin, quint64 &sel_end, quint64 top, int count_line, uint size)
208 if (!cursor->Selected) {
212 sel_begin = min(cursor->SelBegin, cursor->SelEnd);
213 sel_end = max(cursor->SelBegin, cursor->SelEnd);
215 if (top <= sel_end && sel_begin <= max(top + (HexConfig::Num * count_line), top + size)) {
222 inline bool HexView::isSelected(quint64 pos)
224 const quint64 sel_begin = min(cursor->SelBegin, cursor->SelEnd);
225 const quint64 sel_end = max(cursor->SelBegin, cursor->SelEnd);
226 return sel_begin <= pos && pos < sel_end;
229 void HexView::drawLines(QPainter &painter, DCIList &dcolors, int y, int x_begin, int x_end)
231 int index_data = 0, x = 0;
232 bool reset_color = true;
237 for (DCIList::iterator itr_color = dcolors.begin(); itr_color != dcolors.end(); ) {
238 // Setup/Update color settings
240 // Create brush for background
241 brush = QBrush(config.Colors[itr_color->BackgroundColor]);
243 painter.setBackground(brush);
244 painter.setPen(config.Colors[itr_color->TextColor]);
249 if (x < x_begin || x_end <= x) {
254 painter.fillRect(config.x(x), y, config.byteWidth(), config.byteHeight(), brush);
257 byteToHex(buff_[index_data], hex);
258 drawText(painter, hex, config.x(x) + config.ByteMargin.left(), y + config.ByteMargin.top());
262 x = (x + 1) % HexConfig::Num;
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(quint64 pos, int height_max)
293 // Check out of range
294 if (!(config.top() + config.byteHeight() < height_max)) {
300 painter.begin(&pix_);
301 painter.setFont(config.Font);
303 // Get caret coordinates
304 const int x = pos % HexConfig::Num;
305 const int y = config.top() + config.byteHeight() * (pos / HexConfig::Num - cursor->Top);
308 drawCaretShape(CaretDrawInfo(painter, pos, x, y, pos < document->length()));
310 // Finish paint and update screen buffer
312 update(config.x(x), y, config.byteWidth(), config.charHeight());
315 void HexView::drawCaretShape(CaretDrawInfo info)
317 if (info.caret_middle) {
318 // Copy from document
320 document->get(info.pos, &data, 1);
323 byteToHex(data, info.hex);
326 switch (cursor->CaretShape) {
331 drawCaretBlock(info);
334 drawCaretFrame(info);
337 drawCaretUnderbar(info);
342 void HexView::drawCaretLine(const CaretDrawInfo &info)
345 if (cursor->CaretHigh || !info.caret_middle) {
346 x = config.x(info.x);
348 x = config.x(info.x) + config.ByteMargin.left() + config.charWidth();
350 QBrush brush(config.Colors[Color::CaretBackground]);
351 info.painter.fillRect(x, info.y, 2, config.byteHeight(), brush);
354 void HexView::drawCaretUnderbar(const CaretDrawInfo &info)
357 if (cursor->CaretHigh || !info.caret_middle) {
358 width = config.byteWidth() - 1;
359 x = config.x(info.x);
361 width = config.charWidth() + config.ByteMargin.right() - 1;
362 x = config.x(info.x) + config.ByteMargin.left() + config.charWidth();
365 QBrush brush(config.Colors[Color::CaretBackground]);
366 info.painter.fillRect(x, info.y + config.byteHeight() - 2, width, 2, brush);
369 void HexView::drawCaretFrame(const CaretDrawInfo &info)
372 if (cursor->CaretHigh || !info.caret_middle) {
373 width = config.byteWidth() - 1;
374 x = config.x(info.x);
376 width = config.charWidth() + config.ByteMargin.right() - 1;
377 x = config.x(info.x) + config.charWidth() + config.ByteMargin.left();
379 info.painter.setPen(config.Colors[Color::CaretBackground]);
380 info.painter.drawRect(x, info.y, width, config.byteHeight() - 1);
383 void HexView::drawCaretBlock(CaretDrawInfo &info)
385 if (info.caret_middle) {
386 if (cursor->CaretHigh) {
388 QBrush brush(config.Colors[Color::CaretBackground]);
389 info.painter.setBackground(brush);
390 info.painter.setPen(config.Colors[Color::CaretText]);
391 info.painter.fillRect(config.x(info.x), info.y, config.byteWidth(), config.byteHeight(), brush);
392 info.painter.drawText(config.x(info.x) + config.ByteMargin.left(), info.y + config.ByteMargin.top(), config.charWidth(2), config.charHeight(), Qt::AlignCenter, info.hex);
395 QBrush brush(config.Colors[Color::CaretBackground]);
396 info.painter.setBackground(brush);
397 info.painter.setPen(config.Colors[Color::CaretText]);
398 info.painter.fillRect(config.x(info.x) + config.ByteMargin.left() + config.charWidth(), info.y, config.charWidth() + config.ByteMargin.right(), config.byteHeight(), brush);
399 info.hex.remove(0, 1);
400 info.painter.drawText(config.x(info.x) + config.ByteMargin.left() + config.charWidth(), info.y + config.ByteMargin.top(), config.charWidth(2), config.charHeight(), Qt::AlignLeft, info.hex);
403 // Draw caret without data
404 QBrush brush(config.Colors[Color::CaretBackground]);
405 info.painter.fillRect(config.x(info.x), info.y, config.byteWidth(), config.byteHeight(), brush);
409 void HexView::drawCaret(bool visible)
412 drawCaret(cursor->Position, height());
413 emit caretChanged(visible, cursor->Position);
415 drawCaret(false, cursor->Position);
419 void HexView::drawCaret(bool visible, quint64 pos)
423 drawCaret(pos, height());
425 // Set caret invisible
426 quint64 line = cursor->Position / HexConfig::Num;
427 if (cursor->Top <= line && line - cursor->Top < config.drawableLines(height())) {
428 drawView(DRAW_LINE, line - cursor->Top);
432 // Send carete refresh event
433 emit caretChanged(visible, pos);
436 void HexView::byteToHex(uchar c, QString &h)
438 const uchar H = (c >> 4) & 0xF;
440 h[0] = QChar('0' + H);
442 h[0] = QChar('A' + H - 10);
444 const uchar L = c & 0xF;
446 h[1] = QChar('0' + L);
448 h[1] = QChar('A' + L - 10);
452 void HexView::mousePressEvent(QMouseEvent *ev)
454 if (ev->button() == Qt::LeftButton) {
455 qDebug("mosue press pos:%llu end:%llu endO:%llu el:%llu", cursor->Position, cursor->SelEnd, cursor->SelEndOld, cursor->SelEnd / HexConfig::Num);
456 // Draw selected lines
459 // Set begin position
460 cursor->SelEndOld = cursor->Position;
461 cursor->SelBegin = cursor->SelEnd = moveByMouse(ev->pos().x(), ev->pos().y());
464 cursor->Toggle = true;
466 // -- Redraw lines if caret moved
467 if (config.EnableCaret && cursor->SelEnd != cursor->SelEndOld) {
468 const int pos = (cursor->SelEndOld / HexConfig::Num) - cursor->Top;
469 if (pos <= config.drawableLines(height())) {
470 drawView(DRAW_RANGE, pos, pos + 1);
476 // Start mouse capture
481 void HexView::mouseMoveEvent(QMouseEvent *ev)
483 // Check mouse captured
484 if (!cursor->Toggle) {
488 qDebug("mouse move");
490 // Set moved position to OLD
491 cursor->SelEndOld = cursor->SelEnd;
493 // FIXME: move down automatically
494 if (height() < ev->pos().y()) {
498 // Set moved position
499 cursor->SelEnd = moveByMouse(ev->pos().x(), ev->pos().y());
502 cursor->refreshSelected();
504 // Redraw updated lines
507 // -- Redraw lines if caret moved
508 if (config.EnableCaret && cursor->SelEnd != cursor->SelEndOld) {
510 cursor->HexCaretVisible = false;
514 void HexView::mouseReleaseEvent(QMouseEvent *ev)
516 // Check mouse captured
517 if (!cursor->Toggle) {
521 qDebug("mouse release");
526 // Set moved position
527 cursor->SelEnd = moveByMouse(ev->pos().x(), ev->pos().y());
528 cursor->refreshSelected();
530 // Set caret invisible
531 cursor->Toggle = false;
533 // Redraw updated lines
536 // -- Redraw lines if caret moved
537 if (config.EnableCaret && cursor->SelEnd != cursor->SelEndOld) {
539 cursor->HexCaretVisible = false;
543 #define MIN(a,b) ((a) < (b) ? (a) : (b))
544 quint64 HexView::moveByMouse(int xx, int yy)
546 int x = config.XToPos(xx);
547 int y = config.YToLine(yy);
556 cursor->Position = MIN((cursor->Top + y) * HexConfig::Num + x, document->length());
557 return cursor->Position;
561 void HexView::drawSelected(bool reset)
564 if (reset && cursor->Selected) {
565 //--- Reset selected lines
566 // Get selected lines
567 begin = min(min(cursor->SelBegin, cursor->SelEnd), cursor->SelEndOld);
568 end = max(max(cursor->SelBegin, cursor->SelEnd), cursor->SelEndOld);
569 const int begin_line = begin / HexConfig::Num - cursor->Top;
570 const int end_line = end / HexConfig::Num - cursor->Top + 1;
571 cursor->Selected = false;
574 drawView(DRAW_RANGE, begin_line, end_line);
575 } else if (cursor->selMoved()) {
576 // Selected range is changing
577 if ((cursor->SelBegin < cursor->SelEndOld && cursor->SelBegin >= cursor->SelEnd ||
578 cursor->SelBegin >= cursor->SelEndOld && cursor->SelBegin < cursor->SelEnd)) {
579 // Crossing between begin and end
580 begin = min(min(cursor->SelBegin, cursor->SelEnd), cursor->SelEndOld);
581 end = max(max(cursor->SelBegin, cursor->SelEnd), cursor->SelEndOld);
584 begin = min(cursor->SelEnd, cursor->SelEndOld);
585 end = max(cursor->SelEnd, cursor->SelEndOld);
588 // Get redrawing lines
589 const int begin_line = begin / HexConfig::Num - cursor->Top;
590 const int end_line = end / HexConfig::Num - cursor->Top + 1;
592 drawView(DRAW_RANGE, begin_line, end_line);
596 // Enable caret blink
597 void HexView::setCaretBlink(bool enable)
599 if (!config.EnableCaret || !config.CaretBlinkTime) {
603 if (cursor->CaretTimerId == 0) {
604 cursor->CaretTimerId = startTimer(config.CaretBlinkTime);
607 if (cursor->CaretTimerId != 0) {
608 killTimer(cursor->CaretTimerId);
609 cursor->CaretTimerId = 0;
614 void HexView::timerEvent(QTimerEvent *ev)
616 if (cursor->CaretTimerId == ev->timerId()) {
618 drawCaret(cursor->HexCaretVisible);
619 cursor->HexCaretVisible = !cursor->HexCaretVisible;
623 void HexView::keyPressEvent(QKeyEvent *ev)
625 // TODO: support keyboard remap
627 quint64 old = cursor->SelEnd;
628 quint64 oldT = cursor->Top;
651 case Qt::Key_PageDown:
657 cursor->SelEndOld = cursor->SelEnd;
659 if (ev->modifiers() != Qt::SHIFT) {
660 cursor->resetSelection();
663 // FIXME: refactoring refresh event
664 if (cursor->SelEnd != old || cursor->Top != oldT) {
669 if (ev->modifiers() != Qt::NoModifier) {
674 HexView::CaretDrawInfo::CaretDrawInfo(QPainter &p, quint64 pos, int x, int y, bool caret_middle)
680 this->caret_middle = caret_middle;