2388c6b380f04524bf7e25afac92b2678bf38263
[metze/wireshark/wip.git] / ui / qt / packet_list.cpp
1 /* packet_list.cpp
2  *
3  * Wireshark - Network traffic analyzer
4  * By Gerald Combs <gerald@wireshark.org>
5  * Copyright 1998 Gerald Combs
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20  */
21
22 #include "config.h"
23
24 #include <glib.h>
25
26 #include <epan/epan.h>
27 #include <epan/epan_dissect.h>
28
29 #include <epan/column-info.h>
30 #include <epan/column.h>
31 #include <epan/packet.h>
32
33 #include "packet_list.h"
34 #include "proto_tree.h"
35 #include "wireshark_application.h"
36 #include "epan/ipproto.h"
37
38 #include "qt_ui_utils.h"
39
40 #include "ui/main_statusbar.h"
41 #include "ui/recent.h"
42 #include "ui/recent_utils.h"
43 #include "ui/ui_util.h"
44
45 #include "wsutil/str_util.h"
46
47 #include "frame_tvbuff.h"
48
49 #include <QTreeWidget>
50 #include <QTabWidget>
51 #include <QTextEdit>
52 #include <QScrollBar>
53 #include <QContextMenuEvent>
54 #include <QMessageBox>
55
56 // If we ever add the ability to open multiple capture files we might be
57 // able to use something like QMap<capture_file *, PacketList *> to match
58 // capture files against packet lists and models.
59 static PacketList *gbl_cur_packet_list = NULL;
60
61 const int max_comments_to_fetch_ = 20000000; // Arbitrary
62
63 guint
64 packet_list_append(column_info *cinfo, frame_data *fdata)
65 {
66     Q_UNUSED(cinfo);
67
68     if (!gbl_cur_packet_list)
69         return 0;
70
71     /* fdata should be filled with the stuff we need
72      * strings are built at display time.
73      */
74     guint visible_pos;
75
76     visible_pos = gbl_cur_packet_list->packetListModel()->appendPacket(fdata);
77     return visible_pos;
78 }
79
80 // Copied from ui/gtk/packet_list.c
81 void packet_list_resize_column(gint col)
82 {
83     // xxx qtshark
84 //    gint col_width;
85 //    const gchar *long_str;
86
87 g_log(NULL, G_LOG_LEVEL_DEBUG, "FIX: packet_list_resize_column %d", col);
88 //    long_str = packet_list_get_widest_column_string(packetlist, col);
89 //    if(!long_str || strcmp("",long_str)==0)
90 //        /* If we get an empty string leave the width unchanged */
91 //        return;
92 //    column = gtk_tree_view_get_column (GTK_TREE_VIEW(packetlist->view), col);
93 //    col_width = get_default_col_size (packetlist->view, long_str);
94 //    gtk_tree_view_column_set_fixed_width(column, col_width);
95 }
96
97 void
98 packet_list_select_first_row(void)
99 {
100     if (!gbl_cur_packet_list)
101         return;
102     gbl_cur_packet_list->goFirstPacket();
103     gbl_cur_packet_list->setFocus();
104 }
105
106 void
107 packet_list_select_last_row(void)
108 {
109     if (!gbl_cur_packet_list)
110         return;
111     gbl_cur_packet_list->goLastPacket();
112     gbl_cur_packet_list->setFocus();
113 }
114
115 /*
116  * Given a frame_data structure, scroll to and select the row in the
117  * packet list corresponding to that frame.  If there is no such
118  * row, return FALSE, otherwise return TRUE.
119  */
120 gboolean
121 packet_list_select_row_from_data(frame_data *fdata_needle)
122 {
123     int row = gbl_cur_packet_list->packetListModel()->visibleIndexOf(fdata_needle);
124     if (row >= 0) {
125         gbl_cur_packet_list->setCurrentIndex(gbl_cur_packet_list->packetListModel()->index(row,0));
126         return TRUE;
127     }
128
129     return FALSE;
130 }
131
132 gboolean
133 packet_list_check_end(void)
134 {
135     if (gbl_cur_packet_list) {
136         QScrollBar *sb = gbl_cur_packet_list->verticalScrollBar();
137         if (sb && sb->isVisible() && sb->value() == sb->maximum()) {
138             return TRUE;
139         }
140     }
141     return FALSE;
142 }
143
144 void
145 packet_list_clear(void)
146 {
147     if (gbl_cur_packet_list) {
148         gbl_cur_packet_list->clear();
149     }
150 }
151
152 void
153 packet_list_enable_color(gboolean enable)
154 {
155     if (gbl_cur_packet_list && gbl_cur_packet_list->packetListModel()) {
156         gbl_cur_packet_list->packetListModel()->setColorEnabled(enable);
157         gbl_cur_packet_list->update();
158     }
159 }
160
161 void
162 packet_list_freeze(void)
163 {
164     if (gbl_cur_packet_list) {
165         gbl_cur_packet_list->freeze();
166     }
167 }
168
169 void
170 packet_list_thaw(void)
171 {
172     if (gbl_cur_packet_list) {
173         gbl_cur_packet_list->thaw();
174     }
175
176     packets_bar_update();
177 }
178
179 void
180 packet_list_recreate_visible_rows(void)
181 {
182     if (gbl_cur_packet_list && gbl_cur_packet_list->packetListModel()) {
183         gbl_cur_packet_list->packetListModel()->recreateVisibleRows();
184     }
185 }
186
187 frame_data *
188 packet_list_get_row_data(gint row)
189 {
190     if (gbl_cur_packet_list && gbl_cur_packet_list->packetListModel()) {
191         return gbl_cur_packet_list->packetListModel()->getRowFdata(row);
192     }
193     return NULL;
194 }
195
196 void
197 packet_list_moveto_end(void)
198 {
199     if (gbl_cur_packet_list)
200         gbl_cur_packet_list->goLastPacket();
201 }
202
203 /* Redraw the packet list *and* currently-selected detail */
204 void
205 packet_list_queue_draw(void)
206 {
207     if (gbl_cur_packet_list)
208         gbl_cur_packet_list->updateAll();
209 }
210
211 void
212 packet_list_recent_write_all(FILE *rf) {
213     if (!gbl_cur_packet_list)
214         return;
215
216     gbl_cur_packet_list->writeRecent(rf);
217 }
218
219 #define MIN_COL_WIDTH_STR "...."
220
221 PacketList::PacketList(QWidget *parent) :
222     QTreeView(parent),
223     proto_tree_(NULL),
224     byte_view_tab_(NULL),
225     cap_file_(NULL),
226     decode_as_(NULL),
227     ctx_column_(-1)
228 {
229     QMenu *submenu, *subsubmenu;
230
231     setItemsExpandable(FALSE);
232     setRootIsDecorated(FALSE);
233     setSortingEnabled(TRUE);
234     setUniformRowHeights(TRUE);
235     setAccessibleName("Packet list");
236     setItemDelegateForColumn(0, &related_packet_delegate_);
237
238     packet_list_model_ = new PacketListModel(this, cap_file_);
239     setModel(packet_list_model_);
240     packet_list_model_->setColorEnabled(recent.packet_list_colorize);
241
242     // XXX We might want to reimplement setParent() and fill in the context
243     // menu there.
244     ctx_menu_.addAction(window()->findChild<QAction *>("actionEditMarkPacket"));
245     ctx_menu_.addAction(window()->findChild<QAction *>("actionEditIgnorePacket"));
246     ctx_menu_.addAction(window()->findChild<QAction *>("actionEditSetTimeReference"));
247     ctx_menu_.addAction(window()->findChild<QAction *>("actionEditTimeShift"));
248     ctx_menu_.addAction(window()->findChild<QAction *>("actionEditPacketComment"));
249
250     ctx_menu_.addSeparator();
251     submenu = new QMenu(tr("Follow..."));
252     ctx_menu_.addMenu(submenu);
253     submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowTCPStream"));
254     submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowUDPStream"));
255     submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowSSLStream"));
256     filter_actions_ << submenu->actions();
257     //    "     <menuitem name='FollowTCPStream' action='/Follow TCP Stream'/>\n"
258     //    "     <menuitem name='FollowUDPStream' action='/Follow UDP Stream'/>\n"
259     //    "     <menuitem name='FollowSSLStream' action='/Follow SSL Stream'/>\n"
260     submenu = new QMenu(tr("SCTP"));
261     ctx_menu_.addMenu(submenu);
262     submenu->addAction(window()->findChild<QAction *>("actionSCTPAnalyseThisAssociation"));
263     submenu->addAction(window()->findChild<QAction *>("actionSCTPShowAllAssociations"));
264     submenu->addAction(window()->findChild<QAction *>("actionSCTPFilterThisAssociation"));
265     filter_actions_ << submenu->actions();
266     ctx_menu_.addSeparator();
267 //    "     <menuitem name='ManuallyResolveAddress' action='/ManuallyResolveAddress'/>\n"
268     ctx_menu_.addSeparator();
269     submenu = new QMenu(tr("Apply as Filter"));
270     ctx_menu_.addMenu(submenu);
271     submenu->addAction(window()->findChild<QAction *>("actionAnalyzeAAFSelected"));
272     submenu->addAction(window()->findChild<QAction *>("actionAnalyzeAAFNotSelected"));
273     submenu->addAction(window()->findChild<QAction *>("actionAnalyzeAAFAndSelected"));
274     submenu->addAction(window()->findChild<QAction *>("actionAnalyzeAAFOrSelected"));
275     submenu->addAction(window()->findChild<QAction *>("actionAnalyzeAAFAndNotSelected"));
276     submenu->addAction(window()->findChild<QAction *>("actionAnalyzeAAFOrNotSelected"));
277     filter_actions_ << submenu->actions();
278     submenu = new QMenu(tr("Prepare a Filter"));
279     ctx_menu_.addMenu(submenu);
280     submenu->addAction(window()->findChild<QAction *>("actionAnalyzePAFSelected"));
281     submenu->addAction(window()->findChild<QAction *>("actionAnalyzePAFNotSelected"));
282     submenu->addAction(window()->findChild<QAction *>("actionAnalyzePAFAndSelected"));
283     submenu->addAction(window()->findChild<QAction *>("actionAnalyzePAFOrSelected"));
284     submenu->addAction(window()->findChild<QAction *>("actionAnalyzePAFAndNotSelected"));
285     submenu->addAction(window()->findChild<QAction *>("actionAnalyzePAFOrNotSelected"));
286     filter_actions_ << submenu->actions();
287     submenu = new QMenu(tr("Colorize with Filter"));
288 //    "     <menu name= 'ConversationFilter' action='/Conversation Filter'>\n"
289 //    "       <menuitem name='Ethernet' action='/Conversation Filter/Ethernet'/>\n"
290 //    "       <menuitem name='IP' action='/Conversation Filter/IP'/>\n"
291 //    "       <menuitem name='TCP' action='/Conversation Filter/TCP'/>\n"
292 //    "       <menuitem name='UDP' action='/Conversation Filter/UDP'/>\n"
293 //    "       <menuitem name='PN-CBA' action='/Conversation Filter/PN-CBA'/>\n"
294 //    "     <menu name= 'ColorizeConversation' action='/Colorize Conversation'>\n"
295 //    "        <menu name= 'Ethernet' action='/Colorize Conversation/Ethernet'>\n"
296 //    "          <menuitem name='Color1' action='/Colorize Conversation/Ethernet/Color 1'/>\n"
297 //    "          <menuitem name='Color2' action='/Colorize Conversation/Ethernet/Color 2'/>\n"
298 //    "          <menuitem name='Color3' action='/Colorize Conversation/Ethernet/Color 3'/>\n"
299 //    "          <menuitem name='Color4' action='/Colorize Conversation/Ethernet/Color 4'/>\n"
300 //    "          <menuitem name='Color5' action='/Colorize Conversation/Ethernet/Color 5'/>\n"
301 //    "          <menuitem name='Color6' action='/Colorize Conversation/Ethernet/Color 6'/>\n"
302 //    "          <menuitem name='Color7' action='/Colorize Conversation/Ethernet/Color 7'/>\n"
303 //    "          <menuitem name='Color8' action='/Colorize Conversation/Ethernet/Color 8'/>\n"
304 //    "          <menuitem name='Color9' action='/Colorize Conversation/Ethernet/Color 9'/>\n"
305 //    "          <menuitem name='Color10' action='/Colorize Conversation/Ethernet/Color 10'/>\n"
306 //    "          <menuitem name='NewColoringRule' action='/Colorize Conversation/Ethernet/New Coloring Rule'/>\n"
307 //    "        <menu name= 'IP' action='/Colorize Conversation/IP'>\n"
308 //    "          <menuitem name='Color1' action='/Colorize Conversation/IP/Color 1'/>\n"
309 //    "          <menuitem name='Color2' action='/Colorize Conversation/IP/Color 2'/>\n"
310 //    "          <menuitem name='Color3' action='/Colorize Conversation/IP/Color 3'/>\n"
311 //    "          <menuitem name='Color4' action='/Colorize Conversation/IP/Color 4'/>\n"
312 //    "          <menuitem name='Color5' action='/Colorize Conversation/IP/Color 5'/>\n"
313 //    "          <menuitem name='Color6' action='/Colorize Conversation/IP/Color 6'/>\n"
314 //    "          <menuitem name='Color7' action='/Colorize Conversation/IP/Color 7'/>\n"
315 //    "          <menuitem name='Color8' action='/Colorize Conversation/IP/Color 8'/>\n"
316 //    "          <menuitem name='Color9' action='/Colorize Conversation/IP/Color 9'/>\n"
317 //    "          <menuitem name='Color10' action='/Colorize Conversation/IP/Color 10'/>\n"
318 //    "          <menuitem name='NewColoringRule' action='/Colorize Conversation/IP/New Coloring Rule'/>\n"
319 //    "        <menu name= 'TCP' action='/Colorize Conversation/TCP'>\n"
320 //    "          <menuitem name='Color1' action='/Colorize Conversation/TCP/Color 1'/>\n"
321 //    "          <menuitem name='Color2' action='/Colorize Conversation/TCP/Color 2'/>\n"
322 //    "          <menuitem name='Color3' action='/Colorize Conversation/TCP/Color 3'/>\n"
323 //    "          <menuitem name='Color4' action='/Colorize Conversation/TCP/Color 4'/>\n"
324 //    "          <menuitem name='Color5' action='/Colorize Conversation/TCP/Color 5'/>\n"
325 //    "          <menuitem name='Color6' action='/Colorize Conversation/TCP/Color 6'/>\n"
326 //    "          <menuitem name='Color7' action='/Colorize Conversation/TCP/Color 7'/>\n"
327 //    "          <menuitem name='Color8' action='/Colorize Conversation/TCP/Color 8'/>\n"
328 //    "          <menuitem name='Color9' action='/Colorize Conversation/TCP/Color 9'/>\n"
329 //    "          <menuitem name='Color10' action='/Colorize Conversation/TCP/Color 10'/>\n"
330 //    "          <menuitem name='NewColoringRule' action='/Colorize Conversation/TCP/New Coloring Rule'/>\n"
331 //    "        <menu name= 'UDP' action='/Colorize Conversation/UDP'>\n"
332 //    "          <menuitem name='Color1' action='/Colorize Conversation/UDP/Color 1'/>\n"
333 //    "          <menuitem name='Color2' action='/Colorize Conversation/UDP/Color 2'/>\n"
334 //    "          <menuitem name='Color3' action='/Colorize Conversation/UDP/Color 3'/>\n"
335 //    "          <menuitem name='Color4' action='/Colorize Conversation/UDP/Color 4'/>\n"
336 //    "          <menuitem name='Color5' action='/Colorize Conversation/UDP/Color 5'/>\n"
337 //    "          <menuitem name='Color6' action='/Colorize Conversation/UDP/Color 6'/>\n"
338 //    "          <menuitem name='Color7' action='/Colorize Conversation/UDP/Color 7'/>\n"
339 //    "          <menuitem name='Color8' action='/Colorize Conversation/UDP/Color 8'/>\n"
340 //    "          <menuitem name='Color9' action='/Colorize Conversation/UDP/Color 9'/>\n"
341 //    "          <menuitem name='Color10' action='/Colorize Conversation/UDP/Color 10'/>\n"
342 //    "          <menuitem name='NewColoringRule' action='/Colorize Conversation/UDP/New Coloring Rule'/>\n"
343 //    "        <menu name= 'PN-CBA' action='/Colorize Conversation/PN-CBA'>\n"
344 //    "          <menuitem name='Color1' action='/Colorize Conversation/PN-CBA/Color 1'/>\n"
345 //    "          <menuitem name='Color2' action='/Colorize Conversation/PN-CBA/Color 2'/>\n"
346 //    "          <menuitem name='Color3' action='/Colorize Conversation/PN-CBA/Color 3'/>\n"
347 //    "          <menuitem name='Color4' action='/Colorize Conversation/PN-CBA/Color 4'/>\n"
348 //    "          <menuitem name='Color5' action='/Colorize Conversation/PN-CBA/Color 5'/>\n"
349 //    "          <menuitem name='Color6' action='/Colorize Conversation/PN-CBA/Color 6'/>\n"
350 //    "          <menuitem name='Color7' action='/Colorize Conversation/PN-CBA/Color 7'/>\n"
351 //    "          <menuitem name='Color8' action='/Colorize Conversation/PN-CBA/Color 8'/>\n"
352 //    "          <menuitem name='Color9' action='/Colorize Conversation/PN-CBA/Color 9'/>\n"
353 //    "          <menuitem name='Color10' action='/Colorize Conversation/PN-CBA/Color 10'/>\n"
354 //    "          <menuitem name='NewColoringRule' action='/Colorize Conversation/PN-CBA/New Coloring Rule'/>\n"
355 //    "     <menu name= 'SCTP' action='/SCTP'>\n"
356 //    "        <menuitem name='AnalysethisAssociation' action='/SCTP/Analyse this Association'/>\n"
357 //    "        <menuitem name='PrepareFilterforthisAssociation' action='/SCTP/Prepare Filter for this Association'/>\n"
358 //    "     <menuitem name='FollowTCPStream' action='/Follow TCP Stream'/>\n"
359 //    "     <menuitem name='FollowUDPStream' action='/Follow UDP Stream'/>\n"
360 //    "     <menuitem name='FollowSSLStream' action='/Follow SSL Stream'/>\n"
361     ctx_menu_.addSeparator();
362 //    "     <menu name= 'Copy' action='/Copy'>\n"
363     submenu = new QMenu(tr("Copy"));
364     ctx_menu_.addMenu(submenu);
365     //    "        <menuitem name='SummaryTxt' action='/Copy/SummaryTxt'/>\n"
366     //    "        <menuitem name='SummaryCSV' action='/Copy/SummaryCSV'/>\n"
367     submenu->addAction(window()->findChild<QAction *>("actionEditCopyAsFilter"));
368     filter_actions_ << window()->findChild<QAction *>("actionEditCopyAsFilter");
369     submenu->addSeparator();
370     subsubmenu = new QMenu(tr("Bytes"));
371     submenu->addMenu(subsubmenu);
372     //    "           <menuitem name='OffsetHexText' action='/Copy/Bytes/OffsetHexText'/>\n"
373     //    "           <menuitem name='OffsetHex' action='/Copy/Bytes/OffsetHex'/>\n"
374     //    "           <menuitem name='PrintableTextOnly' action='/Copy/Bytes/PrintableTextOnly'/>\n"
375     ctx_menu_.addSeparator();
376 //    "           <menuitem name='HexStream' action='/Copy/Bytes/HexStream'/>\n"
377 //    "           <menuitem name='BinaryStream' action='/Copy/Bytes/BinaryStream'/>\n"
378     ctx_menu_.addSeparator();
379 //    "     <menuitem name='ProtocolPreferences' action='/ProtocolPreferences'/>\n"
380     decode_as_ = window()->findChild<QAction *>("actionAnalyzeDecodeAs");
381     ctx_menu_.addAction(decode_as_);
382 //    "     <menuitem name='Print' action='/Print'/>\n"
383 //    "     <menuitem name='ShowPacketinNewWindow' action='/ShowPacketinNewWindow'/>\n"
384
385     g_assert(gbl_cur_packet_list == NULL);
386     gbl_cur_packet_list = this;
387 }
388
389 void PacketList::setProtoTree (ProtoTree *proto_tree) {
390     proto_tree_ = proto_tree;
391
392     connect(proto_tree_, SIGNAL(goToFrame(int)), this, SLOT(goToPacket(int)));
393     connect(proto_tree_, SIGNAL(relatedFrame(int)), this, SLOT(addRelatedFrame(int)));
394 }
395
396 void PacketList::setByteViewTab (ByteViewTab *byte_view_tab) {
397     byte_view_tab_ = byte_view_tab;
398
399     connect(proto_tree_, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
400             byte_view_tab_, SLOT(protoTreeItemChanged(QTreeWidgetItem*)));
401 }
402
403 PacketListModel *PacketList::packetListModel() const {
404     return packet_list_model_;
405 }
406
407 void PacketList::showEvent (QShowEvent *event) {
408     Q_UNUSED(event);
409
410     if (!cap_file_) return;
411
412     for (int i = 0; i < cap_file_->cinfo.num_cols; i++) {
413         int fmt, col_width;
414         const char *long_str;
415
416         fmt = get_column_format(i);
417         long_str = get_column_width_string(fmt, i);
418         if (long_str) {
419             col_width = wsApp->monospaceTextSize(long_str);
420         } else {
421             col_width = wsApp->monospaceTextSize(MIN_COL_WIDTH_STR);
422         }
423         setColumnWidth(i, col_width);
424     }
425 }
426
427 void PacketList::selectionChanged (const QItemSelection & selected, const QItemSelection & deselected) {
428     QTreeView::selectionChanged(selected, deselected);
429
430     if (!cap_file_) return;
431
432     int row = selected.first().top();
433     cf_select_packet(cap_file_, row);
434     related_packet_delegate_.clear();
435     emit packetSelectionChanged();
436
437     if (!cap_file_->edt) return;
438
439     if (proto_tree_ && cap_file_->edt->tree) {
440         proto_tree_->fillProtocolTree(cap_file_->edt->tree);
441         packet_info *pi = &cap_file_->edt->pi;
442         conversation_t *conv = find_conversation(pi->fd->num, &pi->src, &pi->dst, pi->ptype,
443                                                 pi->srcport, pi->destport, 0);
444         if (conv) {
445             related_packet_delegate_.setConversationSpan(conv->setup_frame, conv->last_frame);
446         }
447         viewport()->update();
448     }
449
450     if (byte_view_tab_) {
451         GSList *src_le;
452         struct data_source *source;
453
454         byte_view_tab_->clear();
455
456         for (src_le = cap_file_->edt->pi.data_src; src_le != NULL; src_le = src_le->next) {
457             source = (struct data_source *)src_le->data;
458             byte_view_tab_->addTab(get_data_source_name(source), get_data_source_tvb(source), cap_file_->edt->tree, proto_tree_, (packet_char_enc)cap_file_->current_frame->flags.encoding);
459         }
460         byte_view_tab_->setCurrentIndex(0);
461     }
462 }
463
464 void PacketList::contextMenuEvent(QContextMenuEvent *event)
465 {
466     bool fa_enabled = filter_actions_[0]->isEnabled();
467     QAction *act;
468     gboolean is_tcp = FALSE, is_udp = FALSE;
469
470     /* walk the list of a available protocols in the packet to see what we have */
471     if ((cap_file_ != NULL) && (cap_file_->edt != NULL))
472     {
473         proto_get_frame_protocols(cap_file_->edt->pi.layers, NULL, &is_tcp, &is_udp, NULL, NULL);
474     }
475
476     foreach (act, filter_actions_)
477     {
478         act->setEnabled(true);
479
480         // check SCTP
481         if (act->objectName().contains("SCTP"))
482         {
483             if ((cap_file_ != NULL) && (cap_file_->edt != NULL) && (cap_file_->edt->pi.ipproto == IP_PROTO_SCTP))
484             {
485                 act->setEnabled(true);
486             }
487             else
488             {
489                 act->setEnabled(false);
490             }
491         }
492
493         // check follow stream
494         if (act->text().contains("TCP"))
495         {
496             act->setEnabled(is_tcp);
497         }
498
499
500         if (act->text().contains("UDP"))
501         {
502             act->setEnabled(is_udp);
503         }
504
505
506         if ((cap_file_ != NULL) && act->text().contains("SSL"))
507         {
508             if (epan_dissect_packet_contains_field(cap_file_->edt, "ssl"))
509             {
510                 act->setEnabled(true);
511             }
512             else
513             {
514                 act->setEnabled(false);
515             }
516         }
517     }
518     decode_as_->setData(qVariantFromValue(true));
519     ctx_column_ = columnAt(event->x());
520     ctx_menu_.exec(event->globalPos());
521     ctx_column_ = -1;
522     decode_as_->setData(QVariant());
523     foreach (act, filter_actions_) {
524         act->setEnabled(fa_enabled);
525     }
526 }
527
528 void PacketList::markFramesReady()
529 {
530     packets_bar_update();
531     updateAll();
532 }
533
534 void PacketList::setFrameMark(gboolean set, frame_data *fdata)
535 {
536     if (set)
537         cf_mark_frame(cap_file_, fdata);
538     else
539         cf_unmark_frame(cap_file_, fdata);
540 }
541
542 void PacketList::setFrameIgnore(gboolean set, frame_data *fdata)
543 {
544     if (set)
545         cf_ignore_frame(cap_file_, fdata);
546     else
547         cf_unignore_frame(cap_file_, fdata);
548 }
549
550 void PacketList::setFrameReftime(gboolean set, frame_data *fdata)
551 {
552     if (!fdata || !cap_file_) return;
553     if (set) {
554         fdata->flags.ref_time=1;
555         cap_file_->ref_time_count++;
556     } else {
557         fdata->flags.ref_time=0;
558         cap_file_->ref_time_count--;
559     }
560     cf_reftime_packets(cap_file_);
561     if (!fdata->flags.ref_time && !fdata->flags.passed_dfilter) {
562         cap_file_->displayed_count--;
563         packet_list_model_->recreateVisibleRows();
564     }
565     updateAll();
566 }
567
568 // Redraw the packet list and detail
569 void PacketList::updateAll() {
570     update();
571
572     if (!cap_file_) return;
573
574     if (selectedIndexes().length() > 0) {
575         cf_select_packet(cap_file_, selectedIndexes()[0].row());
576     }
577
578     if (cap_file_->edt && cap_file_->edt->tree) {
579         proto_tree_->fillProtocolTree(cap_file_->edt->tree);
580     }
581
582     packet_list_model_->resetColumns();
583 }
584
585 void PacketList::freeze()
586 {
587     setUpdatesEnabled(false);
588     setModel(NULL);
589 }
590
591 void PacketList::thaw()
592 {
593     setModel(packet_list_model_);
594     setUpdatesEnabled(true);
595 }
596
597 void PacketList::clear() {
598     //    packet_history_clear();
599     related_packet_delegate_.clear();
600     packet_list_model_->clear();
601     proto_tree_->clear();
602     byte_view_tab_->clear();
603
604     /* XXX is this correct in all cases?
605      * Reset the sort column, use packetlist as model in case the list is frozen.
606      */
607     sortByColumn(0, Qt::AscendingOrder);
608 }
609
610 void PacketList::writeRecent(FILE *rf) {
611     gint col, width, col_fmt;
612     gchar xalign;
613
614     fprintf (rf, "%s:", RECENT_KEY_COL_WIDTH);
615     for (col = 0; col < packet_list_model_->columnCount(); col++) {
616         if (col > 0) {
617             fprintf (rf, ",");
618         }
619         col_fmt = get_column_format(col);
620         if (col_fmt == COL_CUSTOM) {
621             fprintf (rf, " %%Cus:%s,", get_column_custom_field(col));
622         } else {
623             fprintf (rf, " %s,", col_format_to_string(col_fmt));
624         }
625         width = columnWidth(col);
626         xalign = recent_get_column_xalign (col);
627         if (width == 0) {
628             /* We have not initialized the packet list yet, use old values */
629             width = recent_get_column_width (col);
630         }
631         fprintf (rf, " %d", width);
632         if (xalign != COLUMN_XALIGN_DEFAULT) {
633             fprintf (rf, ":%c", xalign);
634         }
635     }
636     fprintf (rf, "\n");
637
638 }
639
640 bool PacketList::contextMenuActive()
641 {
642     return ctx_column_ >= 0 ? true : false;
643 }
644
645 QString &PacketList::getFilterFromRowAndColumn()
646 {
647     frame_data *fdata;
648     QString &filter = *new QString();
649     int row = currentIndex().row();
650
651     if (!cap_file_ || !packet_list_model_ || ctx_column_ < 0 || ctx_column_ >= cap_file_->cinfo.num_cols) return filter;
652
653     fdata = packet_list_model_->getRowFdata(row);
654
655     if (fdata != NULL) {
656         epan_dissect_t edt;
657
658         if (cf_read_frame(cap_file_, fdata) == -1)
659             return filter; /* error reading the frame */
660         /* proto tree, visible. We need a proto tree if there's custom columns */
661         epan_dissect_init(&edt, cap_file_->epan, have_custom_cols(&cap_file_->cinfo), FALSE);
662         col_custom_prime_edt(&edt, &cap_file_->cinfo);
663
664         epan_dissect_run(&edt, &cap_file_->phdr, frame_tvbuff_new_buffer(fdata, &cap_file_->buf), fdata, &cap_file_->cinfo);
665         epan_dissect_fill_in_columns(&edt, TRUE, TRUE);
666
667         if ((cap_file_->cinfo.col_custom_occurrence[ctx_column_]) ||
668             (strchr (cap_file_->cinfo.col_expr.col_expr_val[ctx_column_], ',') == NULL))
669         {
670             /* Only construct the filter when a single occurrence is displayed
671              * otherwise we might end up with a filter like "ip.proto==1,6".
672              *
673              * Or do we want to be able to filter on multiple occurrences so that
674              * the filter might be calculated as "ip.proto==1 && ip.proto==6"
675              * instead?
676              */
677             if (strlen(cap_file_->cinfo.col_expr.col_expr[ctx_column_]) != 0 &&
678                 strlen(cap_file_->cinfo.col_expr.col_expr_val[ctx_column_]) != 0) {
679                 /* leak a little but safer than ep_ here */
680                 if (cap_file_->cinfo.col_fmt[ctx_column_] == COL_CUSTOM) {
681                     header_field_info *hfi = proto_registrar_get_byname(cap_file_->cinfo.col_custom_field[ctx_column_]);
682                     if (hfi->parent == -1) {
683                         /* Protocol only */
684                         filter.append(cap_file_->cinfo.col_expr.col_expr[ctx_column_]);
685                     } else if (hfi->type == FT_STRING) {
686                         /* Custom string, add quotes */
687                         filter.append(QString("%1 == \"%2\"")
688                                       .arg(cap_file_->cinfo.col_expr.col_expr[ctx_column_])
689                                       .arg(cap_file_->cinfo.col_expr.col_expr_val[ctx_column_]));
690                     }
691                 }
692                 if (filter.isEmpty()) {
693                     filter.append(QString("%1 == %2")
694                                   .arg(cap_file_->cinfo.col_expr.col_expr[ctx_column_])
695                                   .arg(cap_file_->cinfo.col_expr.col_expr_val[ctx_column_]));
696                 }
697             }
698         }
699
700         epan_dissect_cleanup(&edt);
701     }
702
703     return filter;
704 }
705
706 QString PacketList::packetComment()
707 {
708     int row = currentIndex().row();
709     frame_data *fdata;
710     char *pkt_comment;
711
712     if (!cap_file_ || !packet_list_model_) return NULL;
713
714     fdata = packet_list_model_->getRowFdata(row);
715
716     if (!fdata) return NULL;
717
718     pkt_comment = cf_get_comment(cap_file_, fdata);
719
720     return QString(pkt_comment);
721
722     /* XXX, g_free(pkt_comment) */
723 }
724
725 void PacketList::setPacketComment(QString new_comment)
726 {
727     int row = currentIndex().row();
728     frame_data *fdata;
729     gchar *new_packet_comment = new_comment.toUtf8().data();
730
731     if (!cap_file_ || !packet_list_model_) return;
732
733     fdata = packet_list_model_->getRowFdata(row);
734
735     if (!fdata) return;
736
737     /* Check if we are clearing the comment */
738     if(new_comment.isEmpty()) {
739         new_packet_comment = NULL;
740     }
741
742     cf_set_user_packet_comment(cap_file_, fdata, new_packet_comment);
743
744     updateAll();
745 }
746
747 QString PacketList::allPacketComments()
748 {
749     guint32 framenum;
750     frame_data *fdata;
751     QString buf_str;
752
753     if (!cap_file_) return buf_str;
754
755     for (framenum = 1; framenum <= cap_file_->count ; framenum++) {
756         fdata = frame_data_sequence_find(cap_file_->frames, framenum);
757
758         char *pkt_comment = cf_get_comment(cap_file_, fdata);
759
760         if (pkt_comment) {
761             buf_str.append(QString(tr("Frame %1: %2 \n\n")).arg(framenum).arg(pkt_comment));
762             g_free(pkt_comment);
763         }
764         if (buf_str.length() > max_comments_to_fetch_) {
765             buf_str.append(QString(tr("[ Comment text exceeds %1. Stopping. ]"))
766                            .arg(format_size(max_comments_to_fetch_, format_size_unit_bytes|format_size_prefix_si)));
767             return buf_str;
768         }
769     }
770     return buf_str;
771 }
772
773 // Slots
774
775 void PacketList::setCaptureFile(capture_file *cf)
776 {
777     cap_file_ = cf;
778     packet_list_model_->setCaptureFile(cf);
779 }
780
781 void PacketList::goNextPacket(void) {
782     setCurrentIndex(moveCursor(MoveDown, Qt::NoModifier));
783 }
784
785 void PacketList::goPreviousPacket(void) {
786     setCurrentIndex(moveCursor(MoveUp, Qt::NoModifier));
787 }
788
789 void PacketList::goFirstPacket(void) {
790     setCurrentIndex(moveCursor(MoveHome, Qt::NoModifier));
791 }
792
793 void PacketList::goLastPacket(void) {
794     setCurrentIndex(moveCursor(MoveEnd, Qt::NoModifier));
795 }
796
797 // XXX We can jump to the wrong packet if a display filter is applied
798 void PacketList::goToPacket(int packet) {
799     int row = packet_list_model_->packetNumberToRow(packet);
800     if (row > 0) {
801         setCurrentIndex(packet_list_model_->index(row, 0));
802     }
803 }
804
805 void PacketList::markFrame()
806 {
807     int row = currentIndex().row();
808     frame_data *fdata;
809
810     if (!cap_file_ || !packet_list_model_) return;
811
812     fdata = packet_list_model_->getRowFdata(row);
813
814     setFrameMark(!fdata->flags.marked, fdata);
815     markFramesReady();
816 }
817
818 void PacketList::markAllDisplayedFrames(bool set)
819 {
820     guint32 framenum;
821     frame_data *fdata;
822
823     if (!cap_file_ || !packet_list_model_) return;
824
825     for (framenum = 1; framenum <= cap_file_->count; framenum++) {
826         fdata = frame_data_sequence_find(cap_file_->frames, framenum);
827         if (fdata->flags.passed_dfilter)
828             setFrameMark(set, fdata);
829     }
830     markFramesReady();
831 }
832
833 void PacketList::ignoreFrame()
834 {
835     int row = currentIndex().row();
836     frame_data *fdata;
837
838     if (!cap_file_ || !packet_list_model_) return;
839
840     fdata = packet_list_model_->getRowFdata(row);
841
842     setFrameIgnore(!fdata->flags.ignored, fdata);
843     emit packetDissectionChanged();
844 }
845
846 void PacketList::ignoreAllDisplayedFrames(bool set)
847 {
848     guint32 framenum;
849     frame_data *fdata;
850
851     if (!cap_file_ || !packet_list_model_) return;
852
853     for (framenum = 1; framenum <= cap_file_->count; framenum++) {
854         fdata = frame_data_sequence_find(cap_file_->frames, framenum);
855         if (!set || fdata->flags.passed_dfilter)
856             setFrameIgnore(set, fdata);
857     }
858     emit packetDissectionChanged();
859 }
860
861 void PacketList::setTimeReference()
862 {
863     if (!cap_file_) return;
864
865     if (cap_file_->current_frame) {
866         if(recent.gui_time_format != TS_RELATIVE && cap_file_->current_frame->flags.ref_time==0) {
867             int ret = QMessageBox::question(
868                         this,
869                         tr("Change Time Display Format?"),
870                         tr("Time References don't work well with the currently selected Time Display Format.\n"
871                            "Do you want to switch to \"Seconds Since Beginning of Capture\" now?"),
872                         QMessageBox::Yes | QMessageBox::No
873                         );
874             if (ret == QMessageBox::Yes) {
875                 timestamp_set_type(TS_RELATIVE);
876                 recent.gui_time_format  = TS_RELATIVE;
877                 cf_timestamp_auto_precision(cap_file_);
878             }
879         } else {
880             setFrameReftime(!cap_file_->current_frame->flags.ref_time,
881                             cap_file_->current_frame);
882         }
883     }
884     updateAll();
885 }
886
887 void PacketList::unsetAllTimeReferences()
888 {
889     if (!cap_file_) return;
890
891     /* XXX: we might need a progressbar here */
892     guint32 framenum;
893     frame_data *fdata;
894     for (framenum = 1; framenum <= cap_file_->count && cap_file_->ref_time_count > 0; framenum++) {
895         fdata = frame_data_sequence_find(cap_file_->frames, framenum);
896         if (fdata->flags.ref_time == 1) {
897             setFrameReftime(FALSE, fdata);
898         }
899     }
900     updateAll();
901 }
902
903 void PacketList::addRelatedFrame(int related_frame)
904 {
905     related_packet_delegate_.addRelatedFrame(related_frame);
906 }
907
908 /*
909  * Editor modelines
910  *
911  * Local Variables:
912  * c-basic-offset: 4
913  * tab-width: 8
914  * indent-tabs-mode: nil
915  * End:
916  *
917  * ex: set shiftwidth=4 tabstop=8 expandtab:
918  * :indentSize=4:tabSize=8:noTabs=true:
919  */