diff options
-rw-r--r-- | ui/qt/overlay_scroll_bar.cpp | 251 | ||||
-rw-r--r-- | ui/qt/overlay_scroll_bar.h | 33 | ||||
-rw-r--r-- | ui/qt/packet_list.cpp | 113 | ||||
-rw-r--r-- | ui/qt/packet_list.h | 3 |
4 files changed, 237 insertions, 163 deletions
diff --git a/ui/qt/overlay_scroll_bar.cpp b/ui/qt/overlay_scroll_bar.cpp index 351faba3a8..51d077a160 100644 --- a/ui/qt/overlay_scroll_bar.cpp +++ b/ui/qt/overlay_scroll_bar.cpp @@ -21,130 +21,215 @@ #include "overlay_scroll_bar.h" +#include "color_utils.h" + +#include <QMouseEvent> #include <QPainter> +#include <QProxyStyle> #include <QResizeEvent> -#include <QStyle> #include <QStyleOptionSlider> // To do: -// - The slider hole doesn't match up with the slider on OS X + Qt 5.3.2. -// - Instead of drawing the map over the scrollbar we could draw it to the -// right of the scrollbar. Many text editors to this. It would let us -// widen the map a bit, which would in turn let us add frame size or -// timing information. +// - We could graph something useful (e.g. delay times) in packet_map_img_. +// https://www.wireshark.org/lists/ethereal-dev/200011/msg00122.html +// - Properly handle transience. + +// We want a normal scrollbar with space on either side on which we can draw +// and receive mouse events. Adding space using a stylesheet loses native +// styling on Windows. Overriding QProxyStyle::drawComplexControl (which is +// called by QScrollBar::paintEvent) results in odd behavior on Windows. +// +// The best solution so far seems to be to simply create a normal-sized child +// scrollbar, manually position it, and synchronize it with its parent. We +// can then alter the parent's mouse and paint behavior to our heart's +// content. + +class OsbProxyStyle : public QProxyStyle +{ + public: + // Hack to keep the scrollbar from disappearing on OS X. We should + // handle this more gracefully. + virtual int styleHint(StyleHint hint, const QStyleOption *option = Q_NULLPTR, const QWidget *widget = Q_NULLPTR, QStyleHintReturn *returnData = Q_NULLPTR) const { + if (hint == SH_ScrollBar_Transient) return false; + + return QProxyStyle::styleHint(hint, option, widget, returnData); + } +}; OverlayScrollBar::OverlayScrollBar(Qt::Orientation orientation, QWidget *parent) : QScrollBar(orientation, parent = 0), - near_overlay_(QImage()), - far_overlay_(QImage()), + child_sb_(orientation, this), + packet_map_img_(QImage()), + packet_map_width_(0), + start_pos_(-1), + end_pos_(-1), selected_pos_(-1) -{} +{ + setStyle(new OsbProxyStyle); + + child_sb_.raise(); + child_sb_.installEventFilter(this); + + // XXX Do we need to connect anything else? + connect(this, SIGNAL(rangeChanged(int,int)), &child_sb_, SLOT(setRange(int,int))); + connect(this, SIGNAL(valueChanged(int)), &child_sb_, SLOT(setValue(int))); + + connect(&child_sb_, SIGNAL(valueChanged(int)), this, SLOT(setValue(int))); +} QSize OverlayScrollBar::sizeHint() const { - return QSize(QScrollBar::sizeHint().width() + (far_overlay_.width() * 2), QScrollBar::sizeHint().height()); + return QSize(packet_map_width_ + child_sb_.sizeHint().width(), + QScrollBar::sizeHint().height()); } -void OverlayScrollBar::setNearOverlayImage(QImage &overlay_image, int selected_pos) +void OverlayScrollBar::setNearOverlayImage(QImage &overlay_image, int start_pos, int end_pos, int selected_pos) { - near_overlay_ = overlay_image; + int old_width = packet_map_img_.width(); + packet_map_img_ = overlay_image; + start_pos_ = start_pos; + end_pos_ = end_pos; selected_pos_ = selected_pos; - update(); -} -void OverlayScrollBar::setFarOverlayImage(QImage &overlay_image) -{ - int old_width = far_overlay_.width(); - far_overlay_ = overlay_image; - if (old_width != far_overlay_.width()) { + if (old_width != packet_map_img_.width()) { + qreal dp_ratio = 1.0; + #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) + dp_ratio = devicePixelRatio(); + #endif + + packet_map_width_ = packet_map_img_.width() / dp_ratio; + updateGeometry(); } update(); } +void OverlayScrollBar::setMarkedPacketImage(QImage &mp_image) +{ + qreal dp_ratio = 1.0; +#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) + dp_ratio = devicePixelRatio(); +#endif + + marked_packet_img_ = mp_image; + marked_packet_width_ = mp_image.width() / dp_ratio; + + child_sb_.update(); +} + QRect OverlayScrollBar::grooveRect() { QStyleOptionSlider opt; + initStyleOption(&opt); + opt.rect = child_sb_.rect(); - return style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarGroove, this); + return child_sb_.style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarGroove, &child_sb_); +} + +void OverlayScrollBar::resizeEvent(QResizeEvent *event) +{ + QScrollBar::resizeEvent(event); + + child_sb_.move(packet_map_width_, 0); + child_sb_.resize(child_sb_.sizeHint().width(), height()); } void OverlayScrollBar::paintEvent(QPaintEvent *event) { - QScrollBar::paintEvent(event); - if (!near_overlay_.isNull()) { - QRect groove_rect = grooveRect(); - QSize gr_size = groove_rect.size(); - qreal dp_ratio = 1.0; + qreal dp_ratio = 1.0; + QSize pm_size(packet_map_width_, geometry().height()); #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) - dp_ratio = devicePixelRatio(); - gr_size *= dp_ratio; + dp_ratio = devicePixelRatio(); + pm_size *= dp_ratio; #endif - QImage groove_overlay(gr_size, QImage::Format_ARGB32_Premultiplied); - groove_overlay.fill(Qt::transparent); - - // Draw the image supplied by the packet list and apply a mask. - QPainter go_painter(&groove_overlay); - go_painter.setPen(Qt::NoPen); - - int fo_width = far_overlay_.width(); - QRect near_dest(fo_width, 0, gr_size.width() - (fo_width * 2), gr_size.height()); - go_painter.drawImage(near_dest, near_overlay_.scaled(near_dest.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); - if (fo_width > 0) { - QRect far_dest(0, 0, fo_width, gr_size.height()); - go_painter.drawImage(far_dest, far_overlay_); - far_dest.moveLeft(gr_size.width() - fo_width); - go_painter.drawImage(far_dest, far_overlay_.mirrored(true, false)); - } + + QPainter painter(this); + + painter.fillRect(event->rect(), palette().window()); + + if (!packet_map_img_.isNull()) { + QImage packet_map(pm_size, QImage::Format_ARGB32_Premultiplied); + packet_map.fill(Qt::transparent); + + // Draw the image supplied by the packet list. + QPainter pm_painter(&packet_map); + pm_painter.setPen(Qt::NoPen); + + QRect near_dest(0, 0, pm_size.width(), pm_size.height()); + pm_painter.drawImage(near_dest, packet_map_img_.scaled(near_dest.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); // Selected packet indicator - if (selected_pos_ >= 0 && selected_pos_ < near_overlay_.height()) { - int no_pos = near_dest.height() * selected_pos_ / near_overlay_.height(); - go_painter.save(); - go_painter.setBrush(palette().highlight().color()); - go_painter.drawRect(0, no_pos, gr_size.width(), dp_ratio); - go_painter.restore(); + if (selected_pos_ >= 0 && selected_pos_ < packet_map_img_.height()) { + pm_painter.save(); + int no_pos = near_dest.height() * selected_pos_ / packet_map_img_.height(); + pm_painter.setBrush(palette().highlight().color()); + pm_painter.drawRect(0, no_pos, pm_size.width(), dp_ratio); + pm_painter.restore(); } - // Outline - QRect near_outline(near_dest); - near_outline.adjust(0, 0, -1, -1); - go_painter.save(); - QColor no_fg(palette().text().color()); - no_fg.setAlphaF(0.25); - go_painter.setPen(no_fg); - go_painter.drawRect(near_outline); - go_painter.restore(); - - // Punch a hole for the slider. - QStyleOptionSlider opt; - initStyleOption(&opt); - - QRect slider_rect = style()->subControlRect(QStyle::CC_ScrollBar, &opt, QStyle::SC_ScrollBarSlider, this); + // Borders + pm_painter.save(); + QColor border_color(ColorUtils::alphaBlend(palette().text(), palette().window(), 0.25)); + pm_painter.setPen(border_color); + pm_painter.drawLine(near_dest.topLeft(), near_dest.bottomLeft()); + pm_painter.drawLine(near_dest.topRight(), near_dest.bottomRight()); + pm_painter.restore(); + + // Draw the map. #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) - slider_rect.setHeight(slider_rect.height() * devicePixelRatio()); - slider_rect.setWidth(slider_rect.width() * devicePixelRatio()); - slider_rect.moveTop((slider_rect.top() - groove_rect.top()) * devicePixelRatio()); -#else - slider_rect.moveTop(slider_rect.top() - groove_rect.top()); + packet_map.setDevicePixelRatio(dp_ratio); #endif - slider_rect.adjust(fo_width + 1, 1, -1 - fo_width, -1); - - go_painter.save(); - go_painter.setCompositionMode(QPainter::CompositionMode_DestinationIn); - QColor slider_hole(Qt::white); - slider_hole.setAlphaF(0.1); - go_painter.setBrush(slider_hole); - go_painter.drawRect(slider_rect); - go_painter.restore(); - - // Draw over the groove. - QPainter painter(this); + painter.drawImage(0, 0, packet_map); + } +} + +bool OverlayScrollBar::eventFilter(QObject *watched, QEvent *event) +{ + bool ret = false; + if (watched == &child_sb_ && event->type() == QEvent::Paint) { + // Paint the scrollbar first. + child_sb_.event(event); + ret = true; + + if (!marked_packet_img_.isNull()) { + qreal dp_ratio = 1.0; + QRect groove_rect = grooveRect(); #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) - groove_overlay.setDevicePixelRatio(devicePixelRatio()); + dp_ratio = devicePixelRatio(); + groove_rect.setTopLeft(groove_rect.topLeft() * dp_ratio); + groove_rect.setSize(groove_rect.size() * dp_ratio); #endif - painter.drawImage(groove_rect.topLeft(), groove_overlay); + + QImage marked_map(groove_rect.width(), groove_rect.height(), QImage::Format_ARGB32_Premultiplied); + marked_map.fill(Qt::transparent); + + QPainter mm_painter(&marked_map); + mm_painter.setPen(Qt::NoPen); + + QRect far_dest(0, 0, groove_rect.width(), groove_rect.height()); + mm_painter.drawImage(far_dest, marked_packet_img_.scaled(far_dest.size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + + #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) + marked_map.setDevicePixelRatio(dp_ratio); + #endif + QPainter painter(&child_sb_); + painter.drawImage(groove_rect.left(), groove_rect.top(), marked_map); + } + } + + return ret; +} + +void OverlayScrollBar::mouseReleaseEvent(QMouseEvent *event) +{ + QRect pm_r(0, 0, packet_map_width_, height()); + + if (pm_r.contains(event->pos())) { + qreal map_ratio = qreal(end_pos_ - start_pos_) / geometry().height(); + + // Try to put the clicked packet near but not at the top. + setValue((event->pos().y() * map_ratio) + start_pos_ - (pageStep() / 4)); } } diff --git a/ui/qt/overlay_scroll_bar.h b/ui/qt/overlay_scroll_bar.h index d1e37d408c..a9e2d7ad3b 100644 --- a/ui/qt/overlay_scroll_bar.h +++ b/ui/qt/overlay_scroll_bar.h @@ -35,25 +35,46 @@ public: /** Set the "near" overlay image. * @param overlay_image An image containing a 1:1 mapping of nearby - * packet colors to raster lines. + * packet colors to raster lines. It should be sized in device + * pixels. + * @param start_pos The first packet number represented by the image. + * -1 means no packet is selected. + * @param end_pos The last packet number represented by the image. -1 + * means no packet is selected. * @param selected_pos The position of the selected packet within the * image. -1 means no packet is selected. */ - void setNearOverlayImage(QImage &overlay_image, int selected_pos = -1); + void setNearOverlayImage(QImage &overlay_image, int start_pos = -1, int end_pos = -1, int selected_pos = -1); /** Set the "far" overlay image. * @param overlay_image An image showing the position of marked, ignored, - * and reference time packets over the entire packet list. + * and reference time packets over the entire packet list. It + * should be sized in device pixels. + */ + void setMarkedPacketImage(QImage &mp_image); + + + /** The "groove" area of the child scrollbar. */ - void setFarOverlayImage(QImage &overlay_image); QRect grooveRect(); +public slots: + protected: + virtual void resizeEvent(QResizeEvent * event); virtual void paintEvent(QPaintEvent * event); + virtual bool eventFilter(QObject *watched, QEvent *event); + virtual void mousePressEvent(QMouseEvent *) { /* No-op */ } + virtual void mouseReleaseEvent(QMouseEvent * event); private: - QImage near_overlay_; - QImage far_overlay_; + QScrollBar child_sb_; + QImage packet_map_img_; + QImage marked_packet_img_; + int packet_map_width_; + int marked_packet_width_; + int start_pos_; + int end_pos_; int selected_pos_; }; diff --git a/ui/qt/packet_list.cpp b/ui/qt/packet_list.cpp index c775317518..ad417e9f35 100644 --- a/ui/qt/packet_list.cpp +++ b/ui/qt/packet_list.cpp @@ -592,6 +592,13 @@ void PacketList::mousePressEvent (QMouseEvent *event) setAutoScroll(true); } +void PacketList::resizeEvent(QResizeEvent *event) +{ + create_near_overlay_ = true; + create_far_overlay_ = true; + QTreeView::resizeEvent(event); +} + void PacketList::setColumnVisibility() { set_column_visibility_ = true; @@ -857,7 +864,7 @@ void PacketList::clear() { QImage overlay; overlay_sb_->setNearOverlayImage(overlay); - overlay_sb_->setFarOverlayImage(overlay); + overlay_sb_->setMarkedPacketImage(overlay); create_near_overlay_ = true; create_far_overlay_ = true; @@ -1429,7 +1436,7 @@ void PacketList::vScrollBarActionTriggered(int) // Odd (prime?) numbers resulted in fewer scaling artifacts. A multiplier // of 9 washed out colors a little too much. -const int height_multiplier_ = 7; +//const int height_multiplier_ = 7; void PacketList::drawNearOverlay() { if (create_near_overlay_) { @@ -1444,18 +1451,15 @@ void PacketList::drawNearOverlay() #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) dp_ratio = overlay_sb_->devicePixelRatio(); #endif - int o_height = overlay_sb_->height() * dp_ratio * height_multiplier_; + int o_height = overlay_sb_->height() * dp_ratio; int o_rows = qMin(packet_list_model_->rowCount(), o_height); + int o_width = (wsApp->fontMetrics().height() * 2 * dp_ratio) + 2; // 2ems + 1-pixel border on either side. int selected_pos = -1; if (recent.packet_list_colorize && o_rows > 0) { - QImage overlay(1, o_height, QImage::Format_ARGB32_Premultiplied); + QImage overlay(o_width, o_height, QImage::Format_ARGB32_Premultiplied); QPainter painter(&overlay); -#if 0 - QElapsedTimer timer; - timer.start(); -#endif overlay.fill(Qt::transparent); @@ -1469,18 +1473,6 @@ void PacketList::drawNearOverlay() for (int row = start; row < end; row++) { packet_list_model_->ensureRowColorized(row); -#if 0 - // Try to remain responsive for large captures. - if (timer.elapsed() > update_time_) { - wsApp->processEvents(); - if (!cap_file_ || cap_file_->state != FILE_READ_DONE) { - create_overlay_ = true; - return; - } - timer.restart(); - } -#endif - frame_data *fdata = packet_list_model_->getRowFdata(row); const color_t *bgcolor = NULL; if (fdata->color_filter) { @@ -1491,9 +1483,7 @@ void PacketList::drawNearOverlay() int next_line = (row - start) * o_height / o_rows; if (bgcolor) { QColor color(ColorUtils::fromColorT(bgcolor)); - - painter.setPen(color); - painter.drawLine(0, cur_line, 0, next_line); + painter.fillRect(0, cur_line, o_width, next_line - cur_line, color); } cur_line = next_line; } @@ -1511,7 +1501,7 @@ void PacketList::drawNearOverlay() } } - overlay_sb_->setNearOverlayImage(overlay, selected_pos); + overlay_sb_->setNearOverlayImage(overlay, start, end, selected_pos); } else { QImage overlay; overlay_sb_->setNearOverlayImage(overlay); @@ -1528,78 +1518,55 @@ void PacketList::drawFarOverlay() if (!prefs.gui_packet_list_show_minimap) return; + QSize groove_size = overlay_sb_->grooveRect().size(); qreal dp_ratio = 1.0; #if QT_VERSION >= QT_VERSION_CHECK(5, 1, 0) dp_ratio = overlay_sb_->devicePixelRatio(); + groove_size *= dp_ratio; #endif - int o_width = 2 * dp_ratio; - int o_height = overlay_sb_->height() * dp_ratio; + int o_width = groove_size.width(); + int o_height = groove_size.height(); int pl_rows = packet_list_model_->rowCount(); + QImage overlay(o_width, o_height, QImage::Format_ARGB32_Premultiplied); - if (recent.packet_list_colorize && pl_rows > 0) { - // Create a tall image here. OverlayScrollBar will scale it to fit. - QImage overlay(o_width, o_height, QImage::Format_ARGB32_Premultiplied); + // If only there were references from popular culture about getting into + // some sort of groove. + if (!overlay.isNull() && recent.packet_list_colorize && pl_rows > 0) { QPainter painter(&overlay); - painter.setRenderHint(QPainter::Antialiasing); -#if 0 - QElapsedTimer timer; - timer.start(); -#endif - // The default "marked" background is black and the default "ignored" - // background is white. Instead of trying to figure out if our - // available colors will show up, just use the palette's background - // here and foreground below. -#if QT_VERSION < QT_VERSION_CHECK(4, 8, 0) - overlay.fill(palette().base().color().value()); -#else - overlay.fill(palette().base().color()); -#endif - QColor arrow_fg = palette().text().color(); - arrow_fg.setAlphaF(0.3); - painter.setPen(arrow_fg); - painter.setBrush(arrow_fg); + // Draw text-colored tick marks on a transparent background. + // Hopefully no themes use the text color for the groove color. + overlay.fill(Qt::transparent); + + QColor tick_color = palette().text().color(); + tick_color.setAlphaF(0.3); + painter.setPen(tick_color); bool have_far = false; for (int row = 0; row < pl_rows; row++) { -#if 0 - // Try to remain responsive for large captures. - if (timer.elapsed() > update_time_) { - wsApp->processEvents(); - if (!cap_file_ || cap_file_->state != FILE_READ_DONE) { - create_overlay_ = true; - return; - } - timer.restart(); - } -#endif frame_data *fdata = packet_list_model_->getRowFdata(row); - bool marked = false; if (fdata->flags.marked || fdata->flags.ref_time || fdata->flags.ignored) { - marked = true; - } - - if (marked) { - int new_line = (row) * o_height / pl_rows; - - QPointF points[3] = { - QPointF(o_width, new_line), - QPointF(0, new_line - (o_width * 0.7)), - QPointF(0, new_line + (o_width * 0.7)) - }; - painter.drawPolygon(points, 3); + int new_line = row * o_height / pl_rows; + int tick_width = o_width / 3; + // Marked or ignored: left side, time refs: right side. + // XXX Draw ignored ticks in the middle? + int x1 = fdata->flags.ref_time ? o_width - tick_width : 1; + int x2 = fdata->flags.ref_time ? o_width - 1 : tick_width; + + painter.drawLine(x1, new_line, x2, new_line); have_far = true; } } if (have_far) { - overlay_sb_->setFarOverlayImage(overlay); + overlay_sb_->setMarkedPacketImage(overlay); return; } + } else { QImage null_overlay; - overlay_sb_->setFarOverlayImage(null_overlay); + overlay_sb_->setMarkedPacketImage(null_overlay); } } diff --git a/ui/qt/packet_list.h b/ui/qt/packet_list.h index 2c11029bff..aad3d03fb6 100644 --- a/ui/qt/packet_list.h +++ b/ui/qt/packet_list.h @@ -82,7 +82,8 @@ protected: void contextMenuEvent(QContextMenuEvent *event); void timerEvent(QTimerEvent *event); void paintEvent(QPaintEvent *event); - virtual void mousePressEvent (QMouseEvent * event); + virtual void mousePressEvent (QMouseEvent *event); + virtual void resizeEvent(QResizeEvent *event); protected slots: void rowsInserted(const QModelIndex &parent, int start, int end); |