3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
7 * SPDX-License-Identifier: GPL-2.0-or-later
10 #include "print_dialog.h"
11 #include <ui_print_dialog.h>
13 #include <wsutil/utf8_entities.h>
17 #include "ui/packet_range.h"
18 #include "ui/win32/file_dlg_win32.h"
21 #include <QPrintDialog>
22 #include <QPageSetupDialog>
24 #include <QPaintEngine>
26 #include <QMessageBox>
28 #include "main_application.h"
32 // Page element callbacks
36 print_preamble_pd(print_stream_t
*self
, char *, const char *)
38 if (!self
) return false;
39 PrintDialog
*print_dlg
= static_cast<PrintDialog
*>(self
->data
);
40 if (!print_dlg
) return false;
42 return print_dlg
->printHeader();
46 print_line_pd(print_stream_t
*self
, int indent
, const char *line
)
48 if (!self
) return false;
49 PrintDialog
*print_dlg
= static_cast<PrintDialog
*>(self
->data
);
50 if (!print_dlg
) return false;
52 return print_dlg
->printLine(indent
, line
);
56 new_page_pd(print_stream_t
*self
)
58 if (!self
) return false;
59 PrintDialog
*print_dlg
= static_cast<PrintDialog
*>(self
->data
);
60 if (!print_dlg
) return false;
62 return print_dlg
->printHeader();
67 PrintDialog::PrintDialog(QWidget
*parent
, capture_file
*cf
, QString selRange
) :
69 pd_ui_(new Ui::PrintDialog
),
72 preview_(new QPrintPreviewWidget(&printer_
)),
73 print_bt_(new QPushButton(tr("&Print…"))),
80 pd_ui_
->setupUi(this);
81 setWindowTitle(mainApp
->windowTitleString(tr("Print")));
83 pd_ui_
->previewLayout
->insertWidget(0, preview_
, Qt::AlignTop
);
85 preview_
->setMinimumWidth(preview_
->height() / 2);
86 preview_
->setToolTip(pd_ui_
->zoomLabel
->toolTip());
88 // XXX Make these configurable
89 header_font_
.setFamily("Times");
90 header_font_
.setPointSizeF(header_font_
.pointSizeF() * 0.8);
91 packet_font_
= mainApp
->monospaceFont();
92 packet_font_
.setPointSizeF(packet_font_
.pointSizeF() * 0.8);
94 memset(&print_args_
, 0, sizeof(print_args_
));
95 memset(&stream_ops_
, 0, sizeof(stream_ops_
));
97 /* Init the export range */
98 packet_range_init(&print_args_
.range
, cap_file_
);
99 /* Default to displayed packets */
100 print_args_
.range
.process_filtered
= true;
102 stream_ops_
.print_preamble
= print_preamble_pd
;
103 stream_ops_
.print_line
= print_line_pd
;
104 stream_ops_
.new_page
= new_page_pd
;
107 stream_
.ops
= &stream_ops_
;
108 print_args_
.stream
= &stream_
;
110 char *display_basename
= g_filename_display_basename(cap_file_
->filename
);
111 printer_
.setDocName(display_basename
);
112 g_free(display_basename
);
114 pd_ui_
->rangeGroupBox
->initRange(&print_args_
.range
, selRange
);
116 pd_ui_
->buttonBox
->addButton(print_bt_
, QDialogButtonBox::ActionRole
);
117 pd_ui_
->buttonBox
->addButton(tr("Page &Setup…"), QDialogButtonBox::ResetRole
);
118 print_bt_
->setDefault(true);
120 connect(preview_
, SIGNAL(paintRequested(QPrinter
*)), this, SLOT(paintPreview(QPrinter
*)));
121 connect(pd_ui_
->rangeGroupBox
, SIGNAL(rangeChanged()),
122 this, SLOT(checkValidity()));
123 connect(pd_ui_
->formatGroupBox
, SIGNAL(formatChanged()),
124 this, SLOT(checkValidity()));
125 connect(pd_ui_
->formFeedCheckBox
, SIGNAL(toggled(bool)),
126 preview_
, SLOT(updatePreview()));
127 connect(pd_ui_
->bannerCheckBox
, SIGNAL(toggled(bool)),
128 preview_
, SLOT(updatePreview()));
133 PrintDialog::~PrintDialog()
135 packet_range_cleanup(&print_args_
.range
);
139 bool PrintDialog::printHeader()
141 if (!cap_file_
|| !cap_file_
->filename
|| !cur_printer_
|| !cur_painter_
) return false;
142 int page_top
= cur_printer_
->pageLayout().paintRectPixels(cur_printer_
->resolution()).top();
144 if (page_pos_
> page_top
) {
146 // When generating a preview, only generate the first page;
147 // if we're past the first page, stop the printing process.
150 // Second and subsequent pages only
151 cur_printer_
->newPage();
152 page_pos_
= page_top
;
155 if (pd_ui_
->bannerCheckBox
->isChecked()) {
156 QString banner
= tr("%1 %2 total packets, %3 shown")
157 .arg(cap_file_
->filename
)
158 .arg(cap_file_
->count
)
159 .arg(packet_range_count(&print_args_
.range
));
160 cur_painter_
->setFont(header_font_
);
161 cur_painter_
->drawText(0, page_top
, banner
);
163 page_pos_
+= cur_painter_
->fontMetrics().height();
164 cur_painter_
->setFont(packet_font_
);
168 bool PrintDialog::printLine(int indent
, const char *line
)
170 QRect out_rect
, page_rect
;
173 if (!line
|| !cur_printer_
|| !cur_painter_
) return false;
175 /* Prepare the tabs for printing, depending on tree level */
176 out_line
.fill(' ', indent
* 4);
179 page_rect
= cur_printer_
->pageLayout().paintRectPixels(cur_printer_
->resolution());
181 out_rect
= cur_painter_
->boundingRect(page_rect
, Qt::TextWordWrap
, out_line
);
183 if (page_rect
.height() < page_pos_
+ out_rect
.height()) {
185 // We're past the end of the page, so this line will be on
189 // When generating a preview, only generate the first page;
190 // if we're past the first page, stop the printing process.
194 // This is an empty line, so it's a separator; no need to
195 // waste space printing it at the top of a page, as the
196 // page break suffices as a separator.
202 out_rect
.translate(0, page_pos_
);
203 cur_painter_
->drawText(out_rect
, Qt::TextWordWrap
, out_line
);
204 page_pos_
+= out_rect
.height();
210 void PrintDialog::keyPressEvent(QKeyEvent
*event
)
212 // XXX - This differs from the main window but matches other applications (e.g. Mozilla and Safari)
213 switch(event
->key()) {
215 case Qt::Key_Underscore
: // Shifted minus on U.S. keyboards
219 case Qt::Key_Equal
: // Unshifted plus on U.S. keyboards
223 case Qt::Key_ParenRight
: // Shifted 0 on U.S. keyboards
224 // fitInView doesn't grow (at least in Qt 4.8.2) so make sure it shrinks.
225 preview_
->setUpdatesEnabled(false);
226 preview_
->setZoomFactor(1.0);
227 preview_
->fitInView();
228 preview_
->setUpdatesEnabled(true);
232 QDialog::keyPressEvent(event
);
237 void PrintDialog::printPackets(QPrinter
*printer
, bool in_preview
)
241 if (!printer
) return;
243 page_pos_
= printer
->pageLayout().paintRectPixels(printer
->resolution()).top();
244 in_preview_
= in_preview
;
246 /* Fill in our print args */
248 print_args_
.format
= PR_FMT_TEXT
;
249 print_args_
.print_summary
= pd_ui_
->formatGroupBox
->summaryEnabled();
250 print_args_
.print_col_headings
= pd_ui_
->formatGroupBox
->includeColumnHeadingsEnabled();
251 print_args_
.print_hex
= pd_ui_
->formatGroupBox
->bytesEnabled();
252 print_args_
.hexdump_options
= pd_ui_
->formatGroupBox
->getHexdumpOptions();
253 print_args_
.print_formfeed
= pd_ui_
->formFeedCheckBox
->isChecked();
255 print_args_
.print_dissections
= print_dissections_none
;
256 if (pd_ui_
->formatGroupBox
->detailsEnabled()) {
257 if (pd_ui_
->formatGroupBox
->allCollapsedEnabled())
258 print_args_
.print_dissections
= print_dissections_collapsed
;
259 else if (pd_ui_
->formatGroupBox
->asDisplayedEnabled())
260 print_args_
.print_dissections
= print_dissections_as_displayed
;
261 else if (pd_ui_
->formatGroupBox
->allExpandedEnabled())
262 print_args_
.print_dissections
= print_dissections_expanded
;
265 // This should be identical to printer_. However, the QPrintPreviewWidget docs
266 // tell us to draw on the printer handed to us by the paintRequested() signal.
267 cur_printer_
= printer
;
268 cur_painter_
= &painter
;
269 if (!painter
.begin(printer
)) {
270 QMessageBox::warning(this, tr("Print Error"),
271 tr("Unable to print to %1.").arg(printer_
.printerName()),
275 // Don't show a progress bar if we're previewing; if it takes a
276 // significant amount of time to generate a preview of the first
277 // page, We Have A Real Problem
278 cf_print_packets(cap_file_
, &print_args_
, in_preview
? false : true);
284 void PrintDialog::paintPreview(QPrinter
*printer
)
286 printPackets(printer
, true);
289 void PrintDialog::checkValidity()
293 if (!pd_ui_
->rangeGroupBox
->isValid()) enable
= false;
295 if (!pd_ui_
->formatGroupBox
->summaryEnabled() &&
296 !pd_ui_
->formatGroupBox
->detailsEnabled() &&
297 !pd_ui_
->formatGroupBox
->bytesEnabled())
302 print_bt_
->setEnabled(enable
);
303 preview_
->updatePreview();
306 void PrintDialog::on_buttonBox_helpRequested()
308 mainApp
->helpTopicAction(HELP_PRINT_DIALOG
);
311 void PrintDialog::on_buttonBox_clicked(QAbstractButton
*button
)
313 QPrintDialog
*print_dlg
;
314 QPageSetupDialog
*ps_dlg
;
319 switch (pd_ui_
->buttonBox
->buttonRole(button
)) {
320 case QDialogButtonBox::ActionRole
:
323 da_ctx
= set_thread_per_monitor_v2_awareness();
325 print_dlg
= new QPrintDialog(&printer_
, this);
326 result
= print_dlg
->exec();
328 revert_thread_per_monitor_v2_awareness(da_ctx
);
330 if (result
== QDialog::Accepted
) {
331 printPackets(&printer_
, false);
335 case QDialogButtonBox::ResetRole
:
337 da_ctx
= set_thread_per_monitor_v2_awareness();
339 ps_dlg
= new QPageSetupDialog(&printer_
, this);
342 revert_thread_per_monitor_v2_awareness(da_ctx
);
344 preview_
->updatePreview();
346 default: // Help, Cancel