1 /* expert_info_model.cpp
2  * Data model for Expert Info tap data.
3  *
4  * Wireshark - Network traffic analyzer
5  * By Gerald Combs <gerald@wireshark.org>
6  * Copyright 1998 Gerald Combs
7  *
8  * SPDX-License-Identifier: GPL-2.0-or-later
9  */
10 
11 #include <ui/qt/models/expert_info_model.h>
12 #include <ui/qt/models/expert_info_proxy_model.h>
13 #include <ui/qt/utils/color_utils.h>
14 
ExpertInfoProxyModel(QObject * parent)15 ExpertInfoProxyModel::ExpertInfoProxyModel(QObject *parent) : QSortFilterProxyModel(parent),
16     severityMode_(Group)
17 {
18 }
19 
lessThan(const QModelIndex & source_left,const QModelIndex & source_right) const20 bool ExpertInfoProxyModel::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
21 {
22     ExpertPacketItem *left_item,
23                      *right_item;
24     QString leftStr, rightStr;
25     bool checkPacketNumber = false;
26     int compare_ret;
27 
28     if (source_left.parent().isValid() && source_right.parent().isValid()) {
29         left_item = static_cast<ExpertPacketItem*>(source_left.parent().internalPointer());
30         right_item = static_cast<ExpertPacketItem*>(source_right.parent().internalPointer());
31     } else {
32         left_item = static_cast<ExpertPacketItem*>(source_left.internalPointer());
33         right_item = static_cast<ExpertPacketItem*>(source_right.internalPointer());
34     }
35 
36     if ((left_item != NULL) && (right_item != NULL)) {
37         switch (source_left.column())
38         {
39         case colProxySeverity:
40             if (left_item->severity() != right_item->severity()) {
41                 return (left_item->severity() < right_item->severity());
42             }
43 
44             checkPacketNumber = true;
45             break;
46         case colProxySummary:
47             compare_ret = left_item->summary().compare(right_item->summary());
48             if (compare_ret < 0)
49                 return true;
50             if (compare_ret > 0)
51                 return false;
52 
53             checkPacketNumber = true;
54             break;
55         case colProxyGroup:
56             if (left_item->group() != right_item->group()) {
57                 return (left_item->group() < right_item->group());
58             }
59 
60             checkPacketNumber = true;
61             break;
62         case colProxyProtocol:
63             compare_ret = left_item->protocol().compare(right_item->protocol());
64             if (compare_ret < 0)
65                 return true;
66             if (compare_ret > 0)
67                 return false;
68 
69             checkPacketNumber = true;
70             break;
71         case colProxyCount:
72             break;
73         default:
74             break;
75         }
76 
77         if (checkPacketNumber) {
78             return (left_item->packetNum() < right_item->packetNum());
79         }
80     }
81 
82     // fallback to string cmp on other fields
83     return QSortFilterProxyModel::lessThan(source_left, source_right);
84 }
85 
data(const QModelIndex & proxy_index,int role) const86 QVariant ExpertInfoProxyModel::data(const QModelIndex &proxy_index, int role) const
87 {
88     QModelIndex source_index;
89 
90     switch (role)
91     {
92     case Qt::BackgroundRole:
93         {
94         source_index = mapToSource(proxy_index);
95 
96         // only color base row
97         if (!source_index.isValid() || source_index.parent().isValid())
98             return QVariant();
99 
100         ExpertPacketItem* item = static_cast<ExpertPacketItem*>(source_index.internalPointer());
101         if (item == NULL)
102             return QVariant();
103 
104         // provide background color for groups
105         switch(item->severity()) {
106         case(PI_COMMENT):
107             return QBrush(ColorUtils::expert_color_comment);
108         case(PI_CHAT):
109             return QBrush(ColorUtils::expert_color_chat);
110         case(PI_NOTE):
111             return QBrush(ColorUtils::expert_color_note);
112         case(PI_WARN):
113             return QBrush(ColorUtils::expert_color_warn);
114         case(PI_ERROR):
115             return QBrush(ColorUtils::expert_color_error);
116         }
117         }
118         break;
119     case Qt::ForegroundRole:
120         {
121         source_index = mapToSource(proxy_index);
122 
123         // only color base row
124         if (!source_index.isValid() || source_index.parent().isValid())
125             return QVariant();
126 
127         ExpertPacketItem* item = static_cast<ExpertPacketItem*>(source_index.internalPointer());
128         if (item == NULL)
129             return QVariant();
130 
131         // provide foreground color for groups
132         switch(item->severity()) {
133         case(PI_COMMENT):
134         case(PI_CHAT):
135         case(PI_NOTE):
136         case(PI_WARN):
137         case(PI_ERROR):
138             return QBrush(ColorUtils::expert_color_foreground);
139         }
140         }
141         break;
142     case Qt::TextAlignmentRole:
143         switch (proxy_index.column())
144         {
145         case colProxySeverity:
146             //packet number should be right aligned
147             if (source_index.parent().isValid())
148                 return Qt::AlignRight;
149             break;
150         case colProxyCount:
151             return Qt::AlignRight;
152         default:
153             break;
154         }
155         return Qt::AlignLeft;
156 
157     case Qt::DisplayRole:
158         source_index = mapToSource(proxy_index);
159 
160         switch (proxy_index.column())
161         {
162         case colProxySeverity:
163             if (source_index.parent().isValid())
164                 return sourceModel()->data(source_index.sibling(source_index.row(), ExpertInfoModel::colPacket), role);
165 
166             return sourceModel()->data(source_index.sibling(source_index.row(), ExpertInfoModel::colSeverity), role);
167         case colProxySummary:
168             return sourceModel()->data(source_index.sibling(source_index.row(), ExpertInfoModel::colSummary), role);
169         case colProxyGroup:
170             return sourceModel()->data(source_index.sibling(source_index.row(), ExpertInfoModel::colGroup), role);
171         case colProxyProtocol:
172             return sourceModel()->data(source_index.sibling(source_index.row(), ExpertInfoModel::colProtocol), role);
173         case colProxyCount:
174             //only show counts for parent
175             if (!source_index.parent().isValid()) {
176                 //because of potential filtering, count is computed manually
177                 unsigned int count = 0;
178                 ExpertPacketItem *child_item,
179                                  *item = static_cast<ExpertPacketItem*>(source_index.internalPointer());
180                 for (int row = 0; row < item->childCount(); row++) {
181                     child_item = item->child(row);
182                     if (child_item == NULL)
183                         continue;
184                     if (filterAcceptItem(*child_item))
185                         count++;
186                 }
187 
188                 return count;
189             }
190         }
191         break;
192     }
193 
194     return QSortFilterProxyModel::data(proxy_index, role);
195 }
196 
headerData(int section,Qt::Orientation orientation,int role) const197 QVariant ExpertInfoProxyModel::headerData(int section, Qt::Orientation orientation, int role) const
198 {
199     if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
200 
201         switch ((enum ExpertProxyColumn)section) {
202         case colProxySeverity:
203             if (severityMode_ == Packet)
204                 return tr("Packet");
205             else
206                 return tr("Severity");
207         case colProxySummary:
208             return tr("Summary");
209         case colProxyGroup:
210             return tr("Group");
211         case colProxyProtocol:
212             return tr("Protocol");
213         case colProxyCount:
214             return tr("Count");
215         default:
216             break;
217         }
218     }
219     return QVariant();
220 }
221 
columnCount(const QModelIndex &) const222 int ExpertInfoProxyModel::columnCount(const QModelIndex&) const
223 {
224     return colProxyLast;
225 }
226 
filterAcceptItem(ExpertPacketItem & item) const227 bool ExpertInfoProxyModel::filterAcceptItem(ExpertPacketItem& item) const
228 {
229     if (hidden_severities_.contains(item.severity()))
230         return false;
231 
232     if (!textFilter_.isEmpty()) {
233         QRegExp regex(textFilter_, Qt::CaseInsensitive);
234 
235         if (item.protocol().contains(regex))
236             return true;
237 
238         if (item.summary().contains(regex))
239             return true;
240 
241         if (item.colInfo().contains(regex))
242             return true;
243 
244         return false;
245     }
246 
247     return true;
248 }
249 
filterAcceptsRow(int sourceRow,const QModelIndex & sourceParent) const250 bool ExpertInfoProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
251 {
252     QModelIndex severityIdx = sourceModel()->index(sourceRow, ExpertInfoModel::colSeverity, sourceParent);
253     ExpertPacketItem* item = static_cast<ExpertPacketItem*>(severityIdx.internalPointer());
254     if (item == NULL)
255         return true;
256 
257     return filterAcceptItem(*item);
258 }
259 
260 //GUI helpers
setSeverityMode(enum SeverityMode mode)261 void ExpertInfoProxyModel::setSeverityMode(enum SeverityMode mode)
262 {
263     severityMode_ = mode;
264     emit headerDataChanged(Qt::Vertical, 0, 1);
265 }
266 
setSeverityFilter(int severity,bool hide)267 void ExpertInfoProxyModel::setSeverityFilter(int severity, bool hide)
268 {
269     if (hide)
270     {
271         hidden_severities_ << severity;
272     }
273     else
274     {
275         hidden_severities_.removeOne(severity);
276     }
277 
278     invalidateFilter();
279 }
280 
setSummaryFilter(const QString & filter)281 void ExpertInfoProxyModel::setSummaryFilter(const QString &filter)
282 {
283     textFilter_ = filter;
284     invalidateFilter();
285 }
286