/* proto_tree.cpp * * $Id$ * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include #include "proto_tree.h" #include #include #include "wireshark_application.h" #include #include #include #include #include #include QColor expert_color_comment ( 0xb7, 0xf7, 0x74 ); /* Green */ QColor expert_color_chat ( 0x80, 0xb7, 0xf7 ); /* light blue */ QColor expert_color_note ( 0xa0, 0xff, 0xff ); /* bright turquoise */ QColor expert_color_warn ( 0xf7, 0xf2, 0x53 ); /* yellow */ QColor expert_color_error ( 0xff, 0x5c, 0x5c ); /* pale red */ QColor expert_color_foreground ( 0x00, 0x00, 0x00 ); /* black */ QColor hidden_proto_item ( 0x44, 0x44, 0x44 ); /* gray */ /* Fill a single protocol tree item with its string value and set its color. */ static void proto_tree_draw_node(proto_node *node, gpointer data) { field_info *fi = PNODE_FINFO(node); gchar label_str[ITEM_LABEL_LENGTH]; gchar *label_ptr; gboolean is_branch; /* dissection with an invisible proto tree? */ g_assert(fi); if (PROTO_ITEM_IS_HIDDEN(node) && !prefs.display_hidden_proto_items) return; // Fill in our label /* was a free format label produced? */ if (fi->rep) { label_ptr = fi->rep->representation; } else { /* no, make a generic label */ label_ptr = label_str; proto_item_fill_label(fi, label_str); } if (node->first_child != NULL) { is_branch = TRUE; g_assert(fi->tree_type >= 0 && fi->tree_type < num_tree_types); } else { is_branch = FALSE; } if (PROTO_ITEM_IS_GENERATED(node)) { if (PROTO_ITEM_IS_HIDDEN(node)) { label_ptr = g_strdup_printf("<[%s]>", label_ptr); } else { label_ptr = g_strdup_printf("[%s]", label_ptr); } } else if (PROTO_ITEM_IS_HIDDEN(node)) { label_ptr = g_strdup_printf("<%s>", label_ptr); } QTreeWidgetItem *parentItem = (QTreeWidgetItem *)data; QTreeWidgetItem *item; ProtoTree *proto_tree = qobject_cast(parentItem->treeWidget()); item = new QTreeWidgetItem(parentItem, 0); // Set our colors. QPalette pal = QApplication::palette(); if (fi && fi->hfinfo) { if(fi->hfinfo->type == FT_PROTOCOL) { item->setData(0, Qt::BackgroundRole, pal.alternateBase()); } if((fi->hfinfo->type == FT_FRAMENUM) || (FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type))) { QFont font = item->font(0); item->setData(0, Qt::ForegroundRole, pal.link()); font.setUnderline(true); item->setData(0, Qt::FontRole, font); if (fi->hfinfo->type == FT_FRAMENUM) { proto_tree->emitRelatedFrame(fi->value.value.uinteger); } } } // XXX - Add routines to get our severity colors. if(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) { switch(FI_GET_FLAG(fi, PI_SEVERITY_MASK)) { case(PI_COMMENT): item->setData(0, Qt::BackgroundRole, expert_color_comment); break; case(PI_CHAT): item->setData(0, Qt::BackgroundRole, expert_color_chat); break; case(PI_NOTE): item->setData(0, Qt::BackgroundRole, expert_color_note); break; case(PI_WARN): item->setData(0, Qt::BackgroundRole, expert_color_warn); break; case(PI_ERROR): item->setData(0, Qt::BackgroundRole, expert_color_error); break; default: g_assert_not_reached(); } item->setData(0, Qt::ForegroundRole, expert_color_foreground); } item->setText(0, label_ptr); item->setData(0, Qt::UserRole, qVariantFromValue(fi)); if (PROTO_ITEM_IS_GENERATED(node) || PROTO_ITEM_IS_HIDDEN(node)) { g_free(label_ptr); } if (is_branch) { if (tree_expanded(fi->tree_type)) { item->setExpanded(true); } else { item->setExpanded(false); } proto_tree_children_foreach(node, proto_tree_draw_node, item); } } ProtoTree::ProtoTree(QWidget *parent) : QTreeWidget(parent), decode_as_(NULL) { QMenu *submenu, *subsubmenu; setAccessibleName(tr("Packet details")); setUniformRowHeights(true); // XXX We might want to reimplement setParent() and fill in the context // menu there. ctx_menu_.addAction(window()->findChild("actionViewExpandSubtrees")); ctx_menu_.addAction(window()->findChild("actionViewExpandAll")); ctx_menu_.addAction(window()->findChild("actionViewCollapseAll")); ctx_menu_.addSeparator(); // " \n" ctx_menu_.addSeparator(); submenu = new QMenu(tr("Apply as Filter")); ctx_menu_.addMenu(submenu); submenu->addAction(window()->findChild("actionAnalyzeAAFSelected")); submenu->addAction(window()->findChild("actionAnalyzeAAFNotSelected")); submenu->addAction(window()->findChild("actionAnalyzeAAFAndSelected")); submenu->addAction(window()->findChild("actionAnalyzeAAFOrSelected")); submenu->addAction(window()->findChild("actionAnalyzeAAFAndNotSelected")); submenu->addAction(window()->findChild("actionAnalyzeAAFOrNotSelected")); submenu = new QMenu(tr("Prepare a Filter")); ctx_menu_.addMenu(submenu); submenu->addAction(window()->findChild("actionAnalyzePAFSelected")); submenu->addAction(window()->findChild("actionAnalyzePAFNotSelected")); submenu->addAction(window()->findChild("actionAnalyzePAFAndSelected")); submenu->addAction(window()->findChild("actionAnalyzePAFOrSelected")); submenu->addAction(window()->findChild("actionAnalyzePAFAndNotSelected")); submenu->addAction(window()->findChild("actionAnalyzePAFOrNotSelected")); submenu = new QMenu(tr("Colorize with Filter")); ctx_menu_.addMenu(submenu); // " \n" // " \n" // " \n" // " \n" // " \n" // " \n" // " \n" // " \n" // " \n" // " \n" // " \n" // " \n" // " \n" // " \n" // " \n" ctx_menu_.addSeparator(); submenu = new QMenu(tr("Copy")); ctx_menu_.addMenu(submenu); submenu->addAction(window()->findChild("actionEditCopyDescription")); submenu->addAction(window()->findChild("actionEditCopyFieldName")); submenu->addAction(window()->findChild("actionEditCopyValue")); submenu->addSeparator(); submenu->addAction(window()->findChild("actionEditCopyAsFilter")); subsubmenu = new QMenu(tr("Bytes")); submenu->addMenu(subsubmenu); subsubmenu->addSeparator(); // " \n" // " \n" // " \n" // " \n" // " \n" // " \n" // " \n" // " \n" // " \n" // " \n" ctx_menu_.addSeparator(); // " \n" // " \n" // " \n" // " \n" ctx_menu_.addSeparator(); decode_as_ = window()->findChild("actionAnalyzeDecodeAs"); ctx_menu_.addAction(decode_as_); // " \n" // " \n" // " \n" connect(this, SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)), this, SLOT(updateSelectionStatus(QTreeWidgetItem*))); connect(this, SIGNAL(expanded(QModelIndex)), this, SLOT(expand(QModelIndex))); connect(this, SIGNAL(collapsed(QModelIndex)), this, SLOT(collapse(QModelIndex))); connect(this, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(itemDoubleClick(QTreeWidgetItem*, int))); } void ProtoTree::clear() { updateSelectionStatus(NULL); QTreeWidget::clear(); } void ProtoTree::contextMenuEvent(QContextMenuEvent *event) { decode_as_->setData(qVariantFromValue(true)); ctx_menu_.exec(event->globalPos()); decode_as_->setData(QVariant()); } void ProtoTree::fillProtocolTree(proto_tree *protocol_tree) { clear(); setFont(wsApp->monospaceFont()); proto_tree_children_foreach(protocol_tree, proto_tree_draw_node, invisibleRootItem()); } void ProtoTree::emitRelatedFrame(int related_frame) { emit relatedFrame(related_frame); } void ProtoTree::updateSelectionStatus(QTreeWidgetItem* item) { if (item) { field_info *fi; QString item_info; fi = item->data(0, Qt::UserRole).value(); if (!fi || !fi->hfinfo) return; if (fi->hfinfo->blurb != NULL && fi->hfinfo->blurb[0] != '\0') { item_info.append(QString().fromUtf8(fi->hfinfo->blurb)); } else { item_info.append(QString().fromUtf8(fi->hfinfo->name)); } if (!item_info.isEmpty()) { int finfo_length; item_info.append(" (" + QString().fromUtf8(fi->hfinfo->abbrev) + ")"); finfo_length = fi->length + fi->appendix_length; if (finfo_length == 1) { item_info.append(tr(", 1 byte")); } else if (finfo_length > 1) { item_info.append(QString(tr(", %1 bytes")).arg(finfo_length)); } emit protoItemSelected(*new QString()); emit protoItemSelected(NULL); emit protoItemSelected(item_info); emit protoItemSelected(fi); } // else the GTK+ version pushes an empty string as described below. /* * Don't show anything if the field name is zero-length; * the pseudo-field for "proto_tree_add_text()" is such * a field, and we don't want "Text (text)" showing up * on the status line if you've selected such a field. * * XXX - there are zero-length fields for which we *do* * want to show the field name. * * XXX - perhaps the name and abbrev field should be null * pointers rather than null strings for that pseudo-field, * but we'd have to add checks for null pointers in some * places if we did that. * * Or perhaps protocol tree items added with * "proto_tree_add_text()" should have -1 as the field index, * with no pseudo-field being used, but that might also * require special checks for -1 to be added. */ } else { emit protoItemSelected(*new QString()); emit protoItemSelected(NULL); } } void ProtoTree::expand(const QModelIndex & index) { field_info *fi; fi = index.data(Qt::UserRole).value(); g_assert(fi); if(prefs.gui_auto_scroll_on_expand) { ScrollHint scroll_hint = PositionAtTop; if (prefs.gui_auto_scroll_percentage > 66) { scroll_hint = PositionAtBottom; } else if (prefs.gui_auto_scroll_percentage >= 33) { scroll_hint = PositionAtCenter; } scrollTo(index, scroll_hint); } /* * Nodes with "finfo->tree_type" of -1 have no ett_ value, and * are thus presumably leaf nodes and cannot be expanded. */ if (fi->tree_type != -1) { g_assert(fi->tree_type >= 0 && fi->tree_type < num_tree_types); tree_expanded_set(fi->tree_type, TRUE); } } void ProtoTree::collapse(const QModelIndex & index) { field_info *fi; fi = index.data(Qt::UserRole).value(); g_assert(fi); /* * Nodes with "finfo->tree_type" of -1 have no ett_ value, and * are thus presumably leaf nodes and cannot be collapsed. */ if (fi->tree_type != -1) { g_assert(fi->tree_type >= 0 && fi->tree_type < num_tree_types); tree_expanded_set(fi->tree_type, FALSE); } } void ProtoTree::expandSubtrees() { QTreeWidgetItem *top_sel; if (selectedItems().length() < 1) { return; } top_sel = selectedItems()[0]; if (!top_sel) { return; } while (top_sel->parent()) { top_sel = top_sel->parent(); } QTreeWidgetItemIterator iter(top_sel); while (*iter) { if ((*iter) != top_sel && (*iter)->parent() == NULL) { // We found the next top-level item break; } (*iter)->setExpanded(true); iter++; } } void ProtoTree::expandAll() { int i; for(i=0; i < num_tree_types; i++) { tree_expanded_set(i, TRUE); } QTreeWidget::expandAll(); } void ProtoTree::collapseAll() { int i; for(i=0; i < num_tree_types; i++) { tree_expanded_set(i, FALSE); } QTreeWidget::collapseAll(); } void ProtoTree::itemDoubleClick(QTreeWidgetItem *item, int column) { Q_UNUSED(column); field_info *fi; fi = item->data(0, Qt::UserRole).value(); if(fi->hfinfo->type == FT_FRAMENUM) { emit goToFrame(fi->value.value.uinteger); } if(FI_GET_FLAG(fi, FI_URL) && IS_FT_STRING(fi->hfinfo->type)) { gchar *url; url = fvalue_to_string_repr(&fi->value, FTREPR_DISPLAY, NULL); if(url){ // browser_open_url(url); QDesktopServices::openUrl(QUrl(url)); g_free(url); } } } /* * Editor modelines * * Local Variables: * c-basic-offset: 4 * tab-width: 8 * indent-tabs-mode: nil * End: * * ex: set shiftwidth=4 tabstop=8 expandtab: * :indentSize=4:tabSize=8:noTabs=true: */