/* byte_view_text.cpp * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "byte_view_text.h" #include #include "wireshark_application.h" #include #include #include #include // XXX - Use KHexEdit instead? // http://api.kde.org/4.x-api/kdelibs-apidocs/interfaces/khexedit/html/index.html ByteViewText::ByteViewText(QWidget *parent, tvbuff_t *tvb, proto_tree *tree, QTreeWidget *tree_widget, packet_char_enc encoding) : QTextEdit(parent), tvb_(tvb), proto_tree_(tree), tree_widget_(tree_widget), bold_highlight_(false), encoding_(encoding), format_(BYTES_HEX), p_start_(-1), p_end_(-1), f_start_(-1), f_end_(-1), fa_start_(-1), fa_end_(-1), per_line_(16), offset_width_(4) { setReadOnly(true); setUndoRedoEnabled(false); setLineWrapMode(QTextEdit::NoWrap); setState(StateNormal); renderBytes(); } void ByteViewText::setEncoding(packet_char_enc encoding) { encoding_ = encoding; } bool ByteViewText::hasDataSource(tvbuff_t *ds_tvb) { if (ds_tvb != NULL && ds_tvb == tvb_) return true; return false; } void ByteViewText::setProtocolHighlight(int start, int end) { p_start_ = start; p_end_ = end; } void ByteViewText::setFieldHighlight(int start, int end, guint32 mask, int mask_le) { Q_UNUSED(mask); Q_UNUSED(mask_le); f_start_ = start; f_end_ = end; } void ByteViewText::setFieldAppendixHighlight(int start, int end) { fa_start_ = start; fa_end_ = end; } void ByteViewText::renderBytes() { int length; int start_byte = 0; if (!tvb_) { clear(); return; } // XXX Even with updates and undo disabled this is slow. Instead of clearing // and filling in the text each time we should probably fill it in once and // use setExtraSelections to set highlighting. setUpdatesEnabled(false); textCursor().beginEditBlock(); clear(); length = tvb_length(tvb_); for (int off = 0; off < length; off += per_line_) { lineCommon(off); } textCursor().endEditBlock(); if (f_start_ > 0 && f_end_ > 0) { start_byte = f_start_; } else if (p_start_ > 0 && p_end_ > 0) { start_byte = p_start_; } scrollToByte(start_byte); setUpdatesEnabled(true); } // Private #define BYTE_VIEW_SEP 8 /* insert a space every BYTE_VIEW_SEP bytes */ void ByteViewText::lineCommon(const int org_off) { static const guchar hexchars[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; const guint8 *pd; int len; highlight_state state; QString str; int off; guchar c; int byten; int j; g_assert(org_off >= 0); if (!tvb_) return; len = tvb_length(tvb_); pd = tvb_get_ptr(tvb_, 0, -1); state = StateNormal; setState(state); /* Print the line number */ str += QString("%1 ").arg(org_off, offset_width_, 16, QChar('0')); /* Print the hex bit */ for (byten = 0, off = org_off; byten < per_line_; byten++) { highlight_state state_cur = StateNormal; bool add_space = byten > 0; if ((off >= f_start_ && off < f_end_) || (off >= fa_start_ && off < fa_end_)) { state_cur = StateField; } else if (off >= p_start_ && off < p_end_) { state_cur = StateProtocol; } if (state_cur != state) { if (state != StateField && add_space) { add_space = false; str += ' '; /* insert a space every BYTE_VIEW_SEP bytes */ if ((off % BYTE_VIEW_SEP) == 0) str += ' '; } if (flushBytes(str) < 0) return; setState(state_cur); state = state_cur; } if (add_space) { str += ' '; /* insert a space every BYTE_VIEW_SEP bytes */ if ((off % BYTE_VIEW_SEP) == 0) str += ' '; } if (off < len) { switch (format_) { case BYTES_HEX: str += hexchars[(pd[off] & 0xf0) >> 4]; str += hexchars[pd[off] & 0x0f]; break; case BYTES_BITS: /* XXX, bitmask */ for (j = 7; j >= 0; j--) str += (pd[off] & (1 << j)) ? '1' : '0'; break; } } else { switch (format_) { case BYTES_HEX: str += " "; break; case BYTES_BITS: str += " "; break; } } off++; } if (state != StateNormal) { if (flushBytes(str) < 0) return; setState(StateNormal); state = StateNormal; } /* Print some space at the end of the line */ str += " "; /* Print the ASCII bit */ for (byten = 0, off = org_off; byten < per_line_; byten++) { highlight_state state_cur = StateNormal; bool add_space = byten > 0; if ((off >= f_start_ && off < f_end_) || (off >= fa_start_ && off < fa_end_)) { state_cur = StateField; } else if (off >= p_start_ && off < p_end_) { state_cur = StateProtocol; } if (state_cur != state) { if (state != StateField && add_space) { add_space = false; /* insert a space every BYTE_VIEW_SEP bytes */ if ((off % BYTE_VIEW_SEP) == 0) str += ' '; } if (flushBytes(str) < 0) return; setState(state_cur); state = state_cur; } if (add_space) { /* insert a space every BYTE_VIEW_SEP bytes */ if ((off % BYTE_VIEW_SEP) == 0) str += ' '; } if (off < len) { c = (encoding_ == PACKET_CHAR_ENC_CHAR_EBCDIC) ? EBCDIC_to_ASCII1(pd[off]) : pd[off]; str += g_ascii_isprint(c) ? c : '.'; } else str += ' '; off++; } if (str.length() > 0) { if (flushBytes(str) < 0) return; } if (state != StateNormal) { setState(StateNormal); /* state = StateNormal; */ } append(""); } void ByteViewText::setState(ByteViewText::highlight_state state) { QPalette pal = wsApp->palette(); moveCursor(QTextCursor::End); setCurrentFont(wsApp->monospaceFont()); setTextColor(pal.text().color()); setTextBackgroundColor(pal.base().color()); switch (state) { case StateProtocol: setTextBackgroundColor(pal.alternateBase().color()); break; case StateField: if (bold_highlight_) { setCurrentFont(wsApp->monospaceFont(true)); } else { setTextColor(pal.base().color()); setTextBackgroundColor(pal.text().color()); } break; default: break; } } int ByteViewText::flushBytes(QString &str) { if (str.length() < 1) return 0; insertPlainText(str); str.clear(); return str.length(); } void ByteViewText::scrollToByte(int byte) { QTextCursor cursor(textCursor()); cursor.setPosition(0); cursor.setPosition(byte * cursor.block().length() / per_line_); setTextCursor(cursor); ensureCursorVisible(); } int ByteViewText::byteFromRowCol(int row, int col) { /* hex_pos_byte array generated with hex_view_get_byte(0, 0, 0...70) */ static const int hex_pos_byte[70] = { -1, -1, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, -1, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 11, 12, 12, 12, 13, 13, 13, 14, 14, 14, 15, 15, 15, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, -1, 8, 9, 10, 11, 12, 13, 14, 15 }; /* bits_pos_byte array generated with bit_view_get_byte(0, 0, 0...84) */ static const int bits_pos_byte[84] = { -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7 }; int off_col = 1; int off_row; off_row = row * per_line_; if (/* char_x < 0 || */ col < offset_width_) return -1; col -= offset_width_; switch (format_) { case BYTES_BITS: g_return_val_if_fail(col >= 0 && col < (int) G_N_ELEMENTS(bits_pos_byte), -1); off_col = bits_pos_byte[col]; break; case BYTES_HEX: g_return_val_if_fail(col >= 0 && col < (int) G_N_ELEMENTS(hex_pos_byte), -1); off_col = hex_pos_byte[col]; break; } if (col == -1) return -1; return off_row + off_col; } void ByteViewText::mousePressEvent (QMouseEvent * event) { if (event->button() == Qt::LeftButton) { int byte; QTextCursor cursor(cursorForPosition(event->pos())); byte = byteFromRowCol(cursor.blockNumber(), cursor.columnNumber()); if (byte >= 0) { field_info *fi = proto_find_field_from_offset(proto_tree_, byte, tvb_); if (fi && tree_widget_) { // XXX - This should probably be a ProtoTree method. QTreeWidgetItemIterator iter(tree_widget_); while (*iter) { if (fi == (*iter)->data(0, Qt::UserRole).value()) { tree_widget_->setCurrentItem((*iter)); } iter++; } } } } QWidget::mousePressEvent (event); } /* * Editor modelines * * Local Variables: * c-basic-offset: 4 * tab-width: 8 * indent-tabs-mode: nil * End: * * ex: set shiftwidth=4 tabstop=8 expandtab: * :indentSize=4:tabSize=8:noTabs=true: */