1 /* proto_tree_model.cpp
2  *
3  * Wireshark - Network traffic analyzer
4  * By Gerald Combs <gerald@wireshark.org>
5  * Copyright 1998 Gerald Combs
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  */
9 
10 #include <ui/qt/models/proto_tree_model.h>
11 
12 #include <epan/prefs.h>
13 #include <wsutil/wslog.h>
14 
15 #include <ui/qt/utils/color_utils.h>
16 
17 #include <QApplication>
18 #include <QPalette>
19 
20 // To do:
21 // - Add ProtoTreeDelegate
22 // - Add ProtoTreeModel to CaptureFile
23 
ProtoTreeModel(QObject * parent)24 ProtoTreeModel::ProtoTreeModel(QObject * parent) :
25     QAbstractItemModel(parent),
26     root_node_(0)
27 {}
28 
flags(const QModelIndex & index) const29 Qt::ItemFlags ProtoTreeModel::flags(const QModelIndex &index) const
30 {
31     Qt::ItemFlags item_flags = QAbstractItemModel::flags(index);
32     if (rowCount(index) < 1) {
33         item_flags |= Qt::ItemNeverHasChildren;
34     }
35 
36     return item_flags;
37 }
38 
index(int row,int,const QModelIndex & parent) const39 QModelIndex ProtoTreeModel::index(int row, int, const QModelIndex &parent) const
40 {
41     ProtoNode parent_node(root_node_);
42 
43     if (parent.isValid()) {
44         // index is not a top level item.
45         parent_node = protoNodeFromIndex(parent);
46     }
47 
48     if (! parent_node.isValid())
49         return QModelIndex();
50 
51     int cur_row = 0;
52     ProtoNode::ChildIterator kids = parent_node.children();
53     while (kids.element().isValid())
54     {
55         if (cur_row == row)
56             break;
57         cur_row++;
58         kids.next();
59     }
60     if (! kids.element().isValid()) {
61         return QModelIndex();
62     }
63 
64     return createIndex(row, 0, static_cast<void *>(kids.element().protoNode()));
65 }
66 
parent(const QModelIndex & index) const67 QModelIndex ProtoTreeModel::parent(const QModelIndex &index) const
68 {
69     ProtoNode parent_node = protoNodeFromIndex(index).parentNode();
70     return indexFromProtoNode(parent_node);
71 }
72 
rowCount(const QModelIndex & parent) const73 int ProtoTreeModel::rowCount(const QModelIndex &parent) const
74 {
75     if (parent.isValid()) {
76         return protoNodeFromIndex(parent).childrenCount();
77     }
78     return ProtoNode(root_node_).childrenCount();
79 }
80 
81 // The QItemDelegate documentation says
82 // "When displaying items from a custom model in a standard view, it is
83 //  often sufficient to simply ensure that the model returns appropriate
84 //  data for each of the roles that determine the appearance of items in
85 //  views."
86 // We might want to move this to a delegate regardless.
data(const QModelIndex & index,int role) const87 QVariant ProtoTreeModel::data(const QModelIndex &index, int role) const
88 {
89     ProtoNode index_node = protoNodeFromIndex(index);
90     FieldInformation finfo(index_node.protoNode());
91     if (!finfo.isValid()) {
92         return QVariant();
93     }
94 
95     switch (role) {
96     case Qt::DisplayRole:
97         return index_node.labelText();
98     case Qt::BackgroundRole:
99     {
100         switch(finfo.flag(PI_SEVERITY_MASK)) {
101         case(0):
102             break;
103         case(PI_COMMENT):
104             return ColorUtils::expert_color_comment;
105         case(PI_CHAT):
106             return ColorUtils::expert_color_chat;
107         case(PI_NOTE):
108             return ColorUtils::expert_color_note;
109         case(PI_WARN):
110             return ColorUtils::expert_color_warn;
111         case(PI_ERROR):
112             return ColorUtils::expert_color_error;
113         default:
114             ws_warning("Unhandled severity flag: %u", finfo.flag(PI_SEVERITY_MASK));
115         }
116         if (finfo.headerInfo().type == FT_PROTOCOL) {
117             return QApplication::palette().window();
118         }
119         return QApplication::palette().base();
120     }
121     case Qt::ForegroundRole:
122     {
123         if (finfo.flag(PI_SEVERITY_MASK)) {
124             return ColorUtils::expert_color_foreground;
125         }
126         if (finfo.isLink()) {
127             return ColorUtils::themeLinkBrush();
128         }
129         if (finfo.headerInfo().type == FT_PROTOCOL) {
130             return QApplication::palette().windowText();
131         }
132         return QApplication::palette().text();
133     }
134     case Qt::FontRole:
135         if (finfo.isLink()) {
136             QFont font;
137             font.setUnderline(true);
138             return font;
139         }
140     default:
141         break;
142     }
143 
144     return QVariant();
145 }
146 
setRootNode(proto_node * root_node)147 void ProtoTreeModel::setRootNode(proto_node *root_node)
148 {
149     beginResetModel();
150     root_node_ = root_node;
151     endResetModel();
152     if (!root_node) return;
153 
154     int row_count = ProtoNode(root_node_).childrenCount();
155     if (row_count < 1) return;
156     beginInsertRows(QModelIndex(), 0, row_count - 1);
157     endInsertRows();
158 }
159 
protoNodeFromIndex(const QModelIndex & index) const160 ProtoNode ProtoTreeModel::protoNodeFromIndex(const QModelIndex &index) const
161 {
162     return ProtoNode(static_cast<proto_node*>(index.internalPointer()));
163 }
164 
indexFromProtoNode(ProtoNode & index_node) const165 QModelIndex ProtoTreeModel::indexFromProtoNode(ProtoNode &index_node) const
166 {
167     int row = index_node.row();
168 
169     if (!index_node.isValid() || row < 0) {
170         return QModelIndex();
171     }
172 
173     return createIndex(row, 0, static_cast<void *>(index_node.protoNode()));
174 }
175 
176 struct find_hfid_ {
177     int hfid;
178     ProtoNode node;
179 };
180 
foreachFindHfid(proto_node * node,gpointer find_hfid_ptr)181 void ProtoTreeModel::foreachFindHfid(proto_node *node, gpointer find_hfid_ptr)
182 {
183     struct find_hfid_ *find_hfid = (struct find_hfid_ *) find_hfid_ptr;
184     if (PNODE_FINFO(node)->hfinfo->id == find_hfid->hfid) {
185         find_hfid->node = ProtoNode(node);
186         return;
187     }
188     proto_tree_children_foreach(node, foreachFindHfid, find_hfid);
189 }
190 
findFirstHfid(int hf_id)191 QModelIndex ProtoTreeModel::findFirstHfid(int hf_id)
192 {
193     if (!root_node_ || hf_id < 0) return QModelIndex();
194 
195     struct find_hfid_ find_hfid;
196     find_hfid.hfid = hf_id;
197 
198     proto_tree_children_foreach(root_node_, foreachFindHfid, &find_hfid);
199 
200     if (find_hfid.node.isValid()) {
201         return indexFromProtoNode(find_hfid.node);
202     }
203     return QModelIndex();
204 }
205 
206 struct find_field_info_ {
207     field_info *fi;
208     ProtoNode node;
209 };
210 
foreachFindField(proto_node * node,gpointer find_finfo_ptr)211 void ProtoTreeModel::foreachFindField(proto_node *node, gpointer find_finfo_ptr)
212 {
213     struct find_field_info_ *find_finfo = (struct find_field_info_ *) find_finfo_ptr;
214     if (PNODE_FINFO(node) == find_finfo->fi) {
215         find_finfo->node = ProtoNode(node);
216         return;
217     }
218     proto_tree_children_foreach(node, foreachFindField, find_finfo);
219 }
220 
findFieldInformation(FieldInformation * finfo)221 QModelIndex ProtoTreeModel::findFieldInformation(FieldInformation *finfo)
222 {
223     if (!root_node_ || !finfo) return QModelIndex();
224     field_info * fi = finfo->fieldInfo();
225     if (!fi) return QModelIndex();
226 
227     struct find_field_info_ find_finfo;
228     find_finfo.fi = fi;
229 
230     proto_tree_children_foreach(root_node_, foreachFindField, &find_finfo);
231     if (find_finfo.node.isValid()) {
232         return indexFromProtoNode(find_finfo.node);
233     }
234     return QModelIndex();
235 }
236