void ByteViewText::paintEvent(QPaintEvent *) { QPainter painter(viewport()); painter.translate(-horizontalScrollBar()->value() * font_width_, 0); painter.setFont(font()); // Pixel offset of this row int row_y = 0; // Starting byte offset guint offset = (guint) verticalScrollBar()->value() * row_width_; // Clear the area painter.fillRect(viewport()->rect(), palette().base()); // Offset background offset_normal_fg_.setColor(ColorUtils::alphaBlend(palette().windowText(), palette().window(), 0.35)); offset_field_fg_.setColor(ColorUtils::alphaBlend(palette().windowText(), palette().window(), 0.65)); if (show_offset_) { QRect offset_rect = QRect(viewport()->rect()); offset_rect.setWidth(offsetPixels()); painter.fillRect(offset_rect, palette().window()); } if (!tvb_) { return; } // Map window coordinates to byte offsets x_pos_to_column_.clear(); for (guint i = 0; i < row_width_; i++) { int sep_width = (i / separator_interval_) * font_width_; if (show_hex_) { // Hittable pixels extend 1/2 space on either side of the hex digits int pixels_per_byte = (recent.gui_bytes_view == BYTES_HEX ? 3 : 9) * font_width_; int hex_x = offsetPixels() + margin_ + sep_width + (i * pixels_per_byte) - (font_width_ / 2); for (int j = 0; j <= pixels_per_byte; j++) { x_pos_to_column_[hex_x + j] = i; } } if (show_ascii_) { int ascii_x = offsetPixels() + hexPixels() + margin_ + sep_width + (i * font_width_); for (int j = 0; j <= font_width_; j++) { x_pos_to_column_[ascii_x + j] = i; } } } // Data rows int widget_height = height(); painter.save(); while(row_y + line_spacing_ < widget_height && offset < tvb_captured_length(tvb_)) { drawOffsetLine(painter, offset, row_y); offset += row_width_; row_y += line_spacing_; } painter.restore(); }
void tAt5toTextAdapter::getAttributes( At5::tPointStyle &pointStyle, const At5::tShapeRec& /*shapeRec*/, const tMap2dCamera& camera, tTextStyleAttributes& tsa, const tVector2l& /*pixelCoords*/, const bool rotateText, const bool m_DrawIcon, const unsigned short at5Version) { //initialize all local variables to safe values tVector2l offsetMeters(0,0); tVector2l textOffsetMeters(0,0); tVector2l offsetPixels(0,0); tVector2l textOffsetPixels(0,0); //bool dispOffsetRotate = false; /* Treat all At5 text as halo-ed text. This is possible because the tTextRender class ignores transparent halos. Per the At5 specification on page 26, background color must be specified for the point feature to be valid. */ tsa.m_halo = true; /* Map rotations from the camera are in radians, plus At5 rotation assumes positive angles = counter-clockwise rotation. Qt rotation is in degrees (as well as the fast lookup for FloatSin and FloatCos), and works with positive angles = clockwise rotation. The angle is converted to degrees then negated. */ if (rotateText) { //tsa.m_rotationDegrees = -1 * shapeRec.Rotation.m_Val; /*zAxisRotation = -1 * (int) RadiansToDegrees(camera.TargetRotationZ()); tsa.m_rotationDegrees = -1 * camera.ConvertRotationAngle( shapeRec.Rotation.m_Val, -zAxisRotation, tVector2l(shapeRec.Point.m_Val * (long)shapeRec.DataRes.m_Val));*/ } tsa.m_fontSize = GetFontSize( pointStyle.styleAttributes.fontSize, at5Version ); /* Per page 41 of the At5 specification, rotation angles for the shapeRec are in degrees. Experimental results show that the rotation is positive for counter-clockwise rotation. Because Qt (and the TextRender library) work with positive angles = clockwise rotation, the angle is negated. */ /* If the rotation is set, we must add the rotation of the map to the rotation of the text to get the effective rotation of the text. */ if (!rotateText) { tsa.m_rotationDegrees = 0; //sanity check } // Set the bold flag if (pointStyle.styleAttributes.bold == 1) { tsa.m_bold = true; } else { tsa.m_bold = false; } if(m_DrawIcon) { if( pointStyle.styleAttributes.defaultFlag & At5::Attribute( At5::tPointStyleAttributes::eM_ColorRGBA ) ) { tsa.m_color = tAt5Utils::RGBAToColor( pointStyle.styleAttributes.colorRGBA ); } else { // Get the text color from the At5 Feat Def and convert to a QColor tsa.m_color = tAt5Utils::At5GetColor(pointStyle.styleAttributes.color); } if( pointStyle.styleAttributes.defaultFlag & At5::Attribute( At5::tPointStyleAttributes::eM_BackgroundColorRGBA ) ) { tsa.m_bkgColor = tAt5Utils::RGBAToColor( pointStyle.styleAttributes.backgroundColorRGBA ); } else { // Get the background color tsa.m_bkgColor = tAt5Utils::At5GetColor(pointStyle.styleAttributes.backgroundColor); } } else { // Get the text color from the At5 Feat Def and convert to a QColor tsa.m_color = QColor(Qt::black); // Get the background color tsa.m_bkgColor = QColor(Qt::white); } // Figure out the alignment At5::tPointStyleAttributes::eAlignCode tempAlign = pointStyle.styleAttributes.align; if ( tempAlign & 128 )// Special case of not rotating the anchor point { tsa.m_anchorPointNoRotate = true; //dispOffsetRotate = false; assert(false); } else { tsa.m_anchorPointNoRotate = false; //dispOffsetRotate = true; } // Next two lines are debug //assert(dispOffsetRotate); assert(!tsa.m_anchorPointNoRotate); tsa.m_align = At5ToFontTextAlign(tempAlign); offsetPixels.x = 0; offsetPixels.y = 0; assert(!(pointStyle.styleAttributes.defaultFlag & At5::Attribute(At5::tPointStyleAttributes::eM_TextOffset) && pointStyle.styleAttributes.defaultFlag & At5::Attribute(At5::tPointStyleAttributes::eM_TextOffsetMeters))); if(pointStyle.styleAttributes.defaultFlag & At5::Attribute(At5::tPointStyleAttributes::eM_TextOffset)) { textOffsetPixels.x = (long) pointStyle.styleAttributes.textOffset.x; textOffsetPixels.y = (long) pointStyle.styleAttributes.textOffset.y; } else { textOffsetPixels.x = 0; textOffsetPixels.y = 0; } offsetMeters.x = 0; offsetMeters.y = 0; if(pointStyle.styleAttributes.defaultFlag & At5::Attribute(At5::tPointStyleAttributes::eM_TextOffsetMeters)) { textOffsetMeters = tVector2l(pointStyle.styleAttributes.textOffsetMeters.x, pointStyle.styleAttributes.textOffsetMeters.y); if (textOffsetMeters != tVector2l::ZERO) { //only do the math if you have to textOffsetMeters = tVector2l( qRound(textOffsetMeters.x / camera.RangeMMPixelAtTarget()), qRound(textOffsetMeters.y / camera.RangeMMPixelAtTarget()) ) ; } } else { textOffsetMeters.x = 0; textOffsetMeters.y = 0; } //the text offset can only be in meters or pixels. If both are non-zero, something went wrong assert(!(( (textOffsetPixels.x != 0) || (textOffsetPixels.y != 0) ) && ( (textOffsetMeters.x != 0) || (textOffsetMeters.y != 0)))); assert(offsetMeters == tVector2l::ZERO); assert(offsetPixels == tVector2l::ZERO); if ((textOffsetMeters.x == 0) && (textOffsetMeters.y == 0)) { tsa.m_effectiveTextOffset = textOffsetPixels; } else if ((textOffsetMeters.x !=0) || (textOffsetMeters.y != 0)) { tsa.m_effectiveTextOffset = textOffsetMeters; } if (pointStyle.styleAttributes.miscCtrl & 1) { tsa.m_rotationLimited = false; } else { tsa.m_rotationLimited = true; } return; }
int ByteViewText::totalPixels() { return offsetPixels() + hexPixels() + asciiPixels(); }
// Draw a line of byte view text for a given offset. // Text with different styles are split into fragments and passed to // flushOffsetFragment. Font character widths aren't necessarily whole // numbers so we track our X coordinate position using using floats. void ByteViewText::drawOffsetLine(QPainter &painter, const guint offset, const int row_y) { if (!tvb_) { return; } guint tvb_len = tvb_captured_length(tvb_); guint max_pos = qMin(offset + row_width_, tvb_len); const guint8 *pd = tvb_get_ptr(tvb_, 0, -1); static const guchar hexchars[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; QString text; highlight_state state = StateNormal, offset_state = StateOffsetNormal; qreal hex_x = offsetPixels() + margin_; qreal ascii_x = offsetPixels() + hexPixels() + margin_; // Hex if (show_hex_) { for (guint tvb_pos = offset; tvb_pos < max_pos; tvb_pos++) { highlight_state hex_state = StateNormal; bool add_space = tvb_pos != offset; if ((tvb_pos >= f_bound_.first && tvb_pos < f_bound_.second) || (tvb_pos >= fa_bound_.first && tvb_pos < fa_bound_.second)) { hex_state = StateField; offset_state = StateOffsetField; } else if (tvb_pos >= p_bound_.first && tvb_pos < p_bound_.second) { hex_state = StateProtocol; } if (hex_state != state) { if ((state == StateNormal || (state == StateProtocol && hex_state == StateField)) && add_space) { add_space = false; text += ' '; /* insert a space every separator_interval_ bytes */ if ((tvb_pos % separator_interval_) == 0) text += ' '; } hex_x += flushOffsetFragment(painter, hex_x, row_y, state, text); state = hex_state; } if (add_space) { text += ' '; /* insert a space every separator_interval_ bytes */ if ((tvb_pos % separator_interval_) == 0) text += ' '; } switch (recent.gui_bytes_view) { case BYTES_HEX: text += hexchars[(pd[tvb_pos] & 0xf0) >> 4]; text += hexchars[pd[tvb_pos] & 0x0f]; break; case BYTES_BITS: /* XXX, bitmask */ for (int j = 7; j >= 0; j--) text += (pd[tvb_pos] & (1 << j)) ? '1' : '0'; break; } } } if (text.length() > 0) { flushOffsetFragment(painter, hex_x, row_y, state, text); } state = StateNormal; // ASCII if (show_ascii_) { for (guint tvb_pos = offset; tvb_pos < max_pos; tvb_pos++) { highlight_state ascii_state = StateNormal; bool add_space = tvb_pos != offset; if ((tvb_pos >= f_bound_.first && tvb_pos < f_bound_.second) || (tvb_pos >= fa_bound_.first && tvb_pos < fa_bound_.second)) { ascii_state = StateField; offset_state = StateOffsetField; } else if (tvb_pos >= p_bound_.first && tvb_pos < p_bound_.second) { ascii_state = StateProtocol; } if (ascii_state != state) { if ((state == StateNormal || (state == StateProtocol && ascii_state == StateField)) && add_space) { add_space = false; /* insert a space every separator_interval_ bytes */ if ((tvb_pos % separator_interval_) == 0) text += ' '; } ascii_x += flushOffsetFragment(painter, ascii_x, row_y, state, text); state = ascii_state; } if (add_space) { /* insert a space every separator_interval_ bytes */ if ((tvb_pos % separator_interval_) == 0) text += ' '; } guchar c = (encoding_ == PACKET_CHAR_ENC_CHAR_EBCDIC) ? EBCDIC_to_ASCII1(pd[tvb_pos]) : pd[tvb_pos]; text += g_ascii_isprint(c) ? c : '.'; } } if (text.length() > 0) { flushOffsetFragment(painter, ascii_x, row_y, state, text); } // Offset. Must be drawn last in order for offset_state to be set. if (show_offset_) { text = QString("%1").arg(offset, offsetChars(), 16, QChar('0')); flushOffsetFragment(painter, margin_, row_y, offset_state, text); } }
void ByteViewText::paintEvent(QPaintEvent *) { QPainter painter(viewport()); painter.translate(-horizontalScrollBar()->value() * font_width_, 0); // Pixel offset of this row int row_y = 0; // Starting byte offset int offset = verticalScrollBar()->value() * row_width_; // Clear the area painter.fillRect(viewport()->rect(), palette().base()); // Offset background. We want the entire height to be filled. if (show_offset_) { QRect offset_rect = QRect(viewport()->rect()); offset_rect.setWidth(offsetPixels()); painter.fillRect(offset_rect, palette().window()); } if ( data_.isEmpty() ) { return; } // Data rows int widget_height = height(); int leading = fontMetrics().leading(); painter.save(); x_pos_to_column_.clear(); while( (int) (row_y + line_height_) < widget_height && offset < (int) data_.count()) { drawLine(&painter, offset, row_y); offset += row_width_; row_y += line_height_ + leading; } painter.restore(); // We can't do this in drawLine since the next line might draw over our rect. if (!hover_outlines_.isEmpty()) { qreal pen_width = 1.0; QPen ho_pen; QColor ho_color = palette().text().color(); ho_color.setAlphaF(0.5); ho_pen.setColor(ho_color); ho_pen.setWidthF(pen_width); #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) if (devicePixelRatio() > 1) { pen_width = 0.5; } #endif painter.save(); painter.setPen(ho_pen); painter.setBrush(Qt::NoBrush); foreach (QRect ho_rect, hover_outlines_) { // These look good on retina and non-retina displays on macOS. // We might want to use fontMetrics numbers instead. ho_rect.adjust(-1, 0, -1, -1); painter.drawRect(ho_rect); }