61caec2883a49cbc1454c41e6a9a425115307983
[obnox/wireshark/wip.git] / gtk / print_dlg.c
1 /* print_dlg.c
2  * Dialog boxes for printing and exporting to text files
3  *
4  * $Id$
5  *
6  * Ethereal - Network traffic analyzer
7  * By Gerald Combs <gerald@ethereal.com>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include <string.h>
30
31 #include <gtk/gtk.h>
32
33 #include "globals.h"
34 #include "keys.h"
35 #include "print.h"
36 #include <epan/prefs.h>
37 #include "alert_box.h"
38 #include "simple_dialog.h"
39 #include "file_dlg.h"
40 #include "gui_utils.h"
41 #include "dlg_utils.h"
42 #include "main.h"
43 #include <epan/epan_dissect.h>
44 #include <epan/filesystem.h>
45 #ifdef _WIN32
46 #include <io.h>
47 #include "print_mswin.h"
48 #endif
49 #include "compat_macros.h"
50 #include "range_utils.h"
51 #include "help_dlg.h"
52
53
54 /* dialog output action */
55 typedef enum {
56   output_action_print,          /* print text to printer */
57   output_action_export_text,    /* export to plain text */
58   output_action_export_ps,      /* export to postscript */
59   output_action_export_psml,    /* export to packet summary markup language */
60   output_action_export_pdml,    /* export to packet data markup language */
61   output_action_export_csv      /* export to csv file */
62 } output_action_e;
63
64
65 /* On Win32, a GUI application apparently can't use "popen()" (it
66   "returns an invalid file handle, if used in a Windows program,
67   that will cause the program to hang indefinitely"), so we can't
68   use a pipe to a print command to print to a printer.
69
70   Eventually, we should try to use the native Win32 printing API
71   for this (and also use various UNIX printing APIs, when present?).
72 */
73
74 static GtkWidget *
75 open_print_dialog(const char *title, output_action_e action, print_args_t *args);
76 static void print_cmd_toggle_dest(GtkWidget *widget, gpointer data);
77 static void print_cmd_toggle_detail(GtkWidget *widget, gpointer data);
78 static void print_ok_cb(GtkWidget *ok_bt, gpointer parent_w);
79 static void print_destroy_cb(GtkWidget *win, gpointer user_data);
80
81
82
83 #define PRINT_ARGS_KEY            "printer_args"
84
85 #define PRINT_PS_RB_KEY           "printer_ps_radio_button"
86 #define PRINT_PDML_RB_KEY         "printer_pdml_radio_button"
87 #define PRINT_PSML_RB_KEY         "printer_psml_radio_button"
88 #define PRINT_CSV_RB_KEY          "printer_csv_radio_button"
89 #define PRINT_DEST_CB_KEY         "printer_destination_check_button"
90
91 #define PRINT_SUMMARY_CB_KEY      "printer_summary_check_button"
92 #define PRINT_DETAILS_CB_KEY      "printer_details_check_button"
93 #define PRINT_COLLAPSE_ALL_RB_KEY "printer_collapse_all_radio_button"
94 #define PRINT_AS_DISPLAYED_RB_KEY "printer_as_displayed_radio_button"
95 #define PRINT_EXPAND_ALL_RB_KEY   "printer_expand_all_radio_button"
96 #define PRINT_HEX_CB_KEY          "printer_hex_check_button"
97 #define PRINT_FORMFEED_CB_KEY     "printer_formfeed_check_button"
98
99 #define PRINT_BT_KEY              "printer_button"
100
101 #define PRINT_TE_PTR_KEY          "printer_file_te_ptr"
102
103
104 /*
105  * Keep a static pointer to the current "Print" window, if any, so that if
106  * somebody tries to do "File:Print" while there's already a "Print" window
107  * up, we just pop up the existing one, rather than creating a new one.
108  */
109 static GtkWidget *print_win = NULL;
110
111 static print_args_t  print_args;
112
113 static gboolean print_prefs_init = FALSE;
114
115
116 static void
117 file_print_cmd(gboolean print_selected)
118 {
119   print_args_t *args = &print_args;
120
121   if (print_win != NULL) {
122     /* There's already a "Print" dialog box; reactivate it. */
123     reactivate_window(print_win);
124     return;
125   }
126
127   /* get settings from preferences (and other initial values) only once */
128   if(print_prefs_init == FALSE) {
129       print_prefs_init          = TRUE;
130       args->format              = prefs.pr_format;
131       args->to_file             = prefs.pr_dest;
132       args->file                = g_strdup(prefs.pr_file);
133       args->cmd                 = g_strdup(prefs.pr_cmd);
134       args->print_summary       = TRUE;
135       args->print_dissections   = print_dissections_as_displayed;
136       args->print_hex           = FALSE;
137       args->print_formfeed      = FALSE;
138   }
139
140   /* init the printing range */
141   packet_range_init(&args->range);
142
143   if(print_selected) {
144       args->range.process = range_process_selected;
145   }
146   
147   print_win = open_print_dialog("Ethereal: Print", output_action_print, args);
148   SIGNAL_CONNECT(print_win, "destroy", print_destroy_cb, &print_win);
149 }
150
151 void
152 file_print_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
153 {
154     file_print_cmd(FALSE);
155 }
156
157 void
158 file_print_selected_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
159 {
160     file_print_cmd(TRUE);
161 }
162
163 /*
164  * Keep a static pointer to the current "Export text" window, if any, so that if
165  * somebody tries to do "File:Export to text" while there's already a "Export text" window
166  * up, we just pop up the existing one, rather than creating a new one.
167  */
168 static GtkWidget *export_text_win = NULL;
169
170 static print_args_t  export_text_args;
171
172 static gboolean export_text_prefs_init = FALSE;
173
174
175 void
176 export_text_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
177 {
178   print_args_t *args = &export_text_args;
179
180   if (export_text_win != NULL) {
181     /* There's already a "Export text" dialog box; reactivate it. */
182     reactivate_window(export_text_win);
183     return;
184   }
185
186   /* get settings from preferences (and other initial values) only once */
187   if(export_text_prefs_init == FALSE) {
188       export_text_prefs_init    = TRUE;
189       args->format              = PR_FMT_TEXT;
190       args->to_file             = TRUE;
191       args->file                = g_strdup("");
192       args->cmd                 = g_strdup("");
193       args->print_summary       = TRUE;
194       args->print_dissections   = print_dissections_as_displayed;
195       args->print_hex           = FALSE;
196       args->print_formfeed      = FALSE;
197   }
198   
199   /* init the printing range */
200   packet_range_init(&args->range);
201
202   export_text_win = open_print_dialog("Ethereal: Export as \"Plain Text\" File", output_action_export_text, args);
203   SIGNAL_CONNECT(export_text_win, "destroy", print_destroy_cb, &export_text_win);
204 }
205
206
207 /*
208  * Keep a static pointer to the current "Export ps" window, if any, so that if
209  * somebody tries to do "File:Export to ps" while there's already a "Export ps" window
210  * up, we just pop up the existing one, rather than creating a new one.
211  */
212 static GtkWidget *export_ps_win = NULL;
213
214 static print_args_t  export_ps_args;
215
216 static gboolean export_ps_prefs_init = FALSE;
217
218
219 void
220 export_ps_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
221 {
222   print_args_t *args = &export_ps_args;
223
224   if (export_ps_win != NULL) {
225     /* There's already a "Export ps" dialog box; reactivate it. */
226     reactivate_window(export_ps_win);
227     return;
228   }
229
230   /* get settings from preferences (and other initial values) only once */
231   if(export_ps_prefs_init == FALSE) {
232       export_ps_prefs_init      = TRUE;
233       args->format              = PR_FMT_PS;
234       args->to_file             = TRUE;
235       args->file                = g_strdup("");
236       args->cmd                 = g_strdup("");
237       args->print_summary       = TRUE;
238       args->print_dissections   = print_dissections_as_displayed;
239       args->print_hex           = FALSE;
240       args->print_formfeed      = FALSE;
241   }
242   
243   /* init the printing range */
244   packet_range_init(&args->range);
245
246   export_ps_win = open_print_dialog("Ethereal: Export as \"PostScript\" file", output_action_export_ps, args);
247   SIGNAL_CONNECT(export_ps_win, "destroy", print_destroy_cb, &export_ps_win);
248 }
249
250
251 /*
252  * Keep a static pointer to the current "Export psml" window, if any, so that if
253  * somebody tries to do "File:Export to psml" while there's already a "Export psml" window
254  * up, we just pop up the existing one, rather than creating a new one.
255  */
256 static GtkWidget *export_psml_win = NULL;
257
258 static print_args_t  export_psml_args;
259
260 static gboolean export_psml_prefs_init = FALSE;
261
262
263 void
264 export_psml_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
265 {
266   print_args_t *args = &export_psml_args;
267
268   if (export_psml_win != NULL) {
269     /* There's already a "Export psml" dialog box; reactivate it. */
270     reactivate_window(export_psml_win);
271     return;
272   }
273
274   /* get settings from preferences (and other initial values) only once */
275   if(export_psml_prefs_init == FALSE) {
276       export_psml_prefs_init    = TRUE;
277       args->format              = PR_FMT_TEXT;  /* XXX */
278       args->to_file             = TRUE;
279       args->file                = g_strdup("");
280       args->cmd                 = g_strdup("");
281       args->print_summary       = TRUE;
282       args->print_dissections   = print_dissections_as_displayed;
283       args->print_hex           = FALSE;
284       args->print_formfeed      = FALSE;
285   }
286   
287   /* init the printing range */
288   packet_range_init(&args->range);
289
290   export_psml_win = open_print_dialog("Ethereal: Export as \"PSML\" file", output_action_export_psml, args);
291   SIGNAL_CONNECT(export_psml_win, "destroy", print_destroy_cb, &export_psml_win);
292 }
293
294
295 /*
296  * Keep a static pointer to the current "Export pdml" window, if any, so that if
297  * somebody tries to do "File:Export to pdml" while there's already a "Export pdml" window
298  * up, we just pop up the existing one, rather than creating a new one.
299  */
300 static GtkWidget *export_pdml_win = NULL;
301
302 static print_args_t  export_pdml_args;
303
304 static gboolean export_pdml_prefs_init = FALSE;
305
306
307 void
308 export_pdml_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
309 {
310   print_args_t *args = &export_pdml_args;
311
312   if (export_pdml_win != NULL) {
313     /* There's already a "Export pdml" dialog box; reactivate it. */
314     reactivate_window(export_pdml_win);
315     return;
316   }
317
318   /* get settings from preferences (and other initial values) only once */
319   if(export_pdml_prefs_init == FALSE) {
320       export_pdml_prefs_init    = TRUE;
321       args->format              = PR_FMT_TEXT;  /* XXX */
322       args->to_file             = TRUE;
323       args->file                = g_strdup("");
324       args->cmd                 = g_strdup("");
325       args->print_summary       = TRUE;
326       args->print_dissections   = print_dissections_as_displayed;
327       args->print_hex           = FALSE;
328       args->print_formfeed      = FALSE;
329   }
330   
331   /* init the printing range */
332   packet_range_init(&args->range);
333
334   export_pdml_win = open_print_dialog("Ethereal: Export as \"PDML\" file", output_action_export_pdml, args);
335   SIGNAL_CONNECT(export_pdml_win, "destroy", print_destroy_cb, &export_pdml_win);
336 }
337
338 /*
339  * Keep a static pointer to the current "Export csv" window, if any, so that if
340  * somebody tries to do "File:Export to CSV" while there's already a "Export csv" window
341  * up, we just pop up the existing one, rather than creating a new one.
342  */
343 static GtkWidget *export_csv_win = NULL;
344
345 static print_args_t  export_csv_args;
346
347 static gboolean export_csv_prefs_init = FALSE;
348
349 void
350 export_csv_cmd_cb(GtkWidget *widget _U_, gpointer data _U_)
351 {
352   print_args_t *args = &export_csv_args;
353
354   if (export_csv_win != NULL) {
355     /* There's already a "Export csv" dialog box; reactivate it. */
356     reactivate_window(export_csv_win);
357     return;
358   }
359
360   /* get settings from preferences (and other initial values) only once */
361   if(export_csv_prefs_init == FALSE) {
362       export_csv_prefs_init     = TRUE;
363       args->format              = PR_FMT_TEXT; /* XXX */
364       args->to_file             = TRUE;
365       args->file                = g_strdup("");
366       args->cmd                 = g_strdup("");
367       args->print_summary       = FALSE;
368       args->print_dissections   = print_dissections_none;
369       args->print_hex           = FALSE;
370       args->print_formfeed      = FALSE;
371   }
372
373   /* init the printing range */
374   packet_range_init(&args->range);
375
376   export_csv_win = open_print_dialog("Ethereal: Export as \"Comma Separated Values\" File", output_action_export_csv, args);
377   SIGNAL_CONNECT(export_csv_win, "destroy", print_destroy_cb, &export_csv_win);
378 }
379
380 static void
381 print_browse_file_cb(GtkWidget *file_bt, GtkWidget *file_te)
382 {
383     file_selection_browse(file_bt, file_te, "Ethereal: Print to File",
384                           FILE_SELECTION_WRITE_BROWSE);
385 }
386
387
388
389 /* Open the print dialog */
390 static GtkWidget *
391 open_print_dialog(const char *title, output_action_e action, print_args_t *args)
392 {
393 #if GTK_MAJOR_VERSION < 2
394   GtkAccelGroup *accel_group;
395 #endif
396
397   GtkWidget     *main_win;
398   GtkWidget     *main_vb;
399
400   GtkWidget     *printer_fr, *printer_vb, *export_format_lb;
401   GtkWidget     *text_rb, *ps_rb, *pdml_rb, *psml_rb, *csv_rb;
402   GtkWidget     *printer_tb, *dest_cb;
403 #ifndef _WIN32
404   GtkWidget     *cmd_lb, *cmd_te;
405 #endif
406   GtkWidget     *file_bt, *file_te;
407
408   GtkWidget     *range_fr, *range_tb;
409
410   GtkWidget     *packet_hb;
411
412   GtkWidget     *format_fr, *format_vb;
413   GtkWidget     *summary_cb;
414
415   GtkWidget     *details_cb;
416   GtkWidget     *details_hb, *details_vb;
417   GtkWidget     *collapse_all_rb, *as_displayed_rb, *expand_all_rb;
418   GtkWidget     *hex_cb;
419   GtkWidget     *sep, *formfeed_cb;
420
421   GtkWidget     *bbox, *ok_bt, *cancel_bt, *help_bt;
422
423   GtkTooltips   *tooltips;
424
425
426   /* dialog window */
427   main_win = dlg_window_new(title);
428
429   /* Enable tooltips */
430   tooltips = gtk_tooltips_new();
431
432 #if GTK_MAJOR_VERSION < 2
433   /* Accelerator group for the accelerators (or, as they're called in
434      Windows and, I think, in Motif, "mnemonics"; Alt+<key> is a mnemonic,
435      Ctrl+<key> is an accelerator). */
436   accel_group = gtk_accel_group_new();
437   gtk_window_add_accel_group(GTK_WINDOW(main_win), accel_group);
438 #endif
439
440   /* Vertical enclosing container for each row of widgets */
441   main_vb = gtk_vbox_new(FALSE, 5);
442   gtk_container_border_width(GTK_CONTAINER(main_vb), 5);
443   gtk_container_add(GTK_CONTAINER(main_win), main_vb);
444   gtk_widget_show(main_vb);
445
446 /*****************************************************/
447
448   /*** printer frame ***/
449   printer_fr = gtk_frame_new(action == output_action_print ? "Printer" : "Export to file:");
450   gtk_box_pack_start(GTK_BOX(main_vb), printer_fr, FALSE, FALSE, 0);
451   gtk_widget_show(printer_fr);
452   printer_vb = gtk_vbox_new(FALSE, 5);
453   gtk_container_border_width(GTK_CONTAINER(printer_vb), 5);
454   gtk_container_add(GTK_CONTAINER(printer_fr), printer_vb);
455   gtk_widget_show(printer_vb);
456
457   /* "Plain text" / "Postscript" / "PDML", ... radio buttons */
458   text_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(NULL, "Plain _text", accel_group);
459   if (args->format == PR_FMT_TEXT)
460     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(text_rb), TRUE);
461   gtk_tooltips_set_tip (tooltips, text_rb, "Print output in ascii \"plain text\" format. If you're unsure, use this format.", NULL);
462   gtk_box_pack_start(GTK_BOX(printer_vb), text_rb, FALSE, FALSE, 0);
463   if(action == output_action_print)
464     gtk_widget_show(text_rb);
465
466   ps_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(text_rb, "_PostScript", accel_group);
467   if (args->format == PR_FMT_PS)
468     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(ps_rb), TRUE);
469   gtk_tooltips_set_tip (tooltips, ps_rb, "Print output in \"postscript\" format, for postscript capable printers or print servers.", NULL);
470   gtk_box_pack_start(GTK_BOX(printer_vb), ps_rb, FALSE, FALSE, 0);
471   if(action == output_action_print)
472     gtk_widget_show(ps_rb);
473
474   pdml_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(text_rb, "PDM_L (XML: Packet Details Markup Language)", accel_group);
475   if (action == output_action_export_pdml)
476     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(pdml_rb), TRUE);
477   gtk_tooltips_set_tip (tooltips, pdml_rb,
478       "Print output in \"PDML\" (Packet Details Markup Language), "
479       "an XML based packet data interchange format. "
480       "Usually used in combination with the \"Output to file\" option to export packet data into an XML file.", NULL);
481   gtk_box_pack_start(GTK_BOX(printer_vb), pdml_rb, FALSE, FALSE, 0);
482   /* gtk_widget_show(pdml_rb); */
483
484   psml_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(text_rb, "PSML (XML: Packet Summary Markup Language)", accel_group);
485   if (action == output_action_export_psml)
486     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(psml_rb), TRUE);
487   gtk_tooltips_set_tip (tooltips, psml_rb,
488       "Print output in \"PSML\" (Packet Summary Markup Language), "
489       "an XML based packet summary interchange format. "
490       "Usually used in combination with the \"Output to file\" option to export packet data into an XML file.", NULL);
491   gtk_box_pack_start(GTK_BOX(printer_vb), psml_rb, FALSE, FALSE, 0);
492   /* gtk_widget_show(psml_rb); */
493
494   csv_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(text_rb, "_CSV", accel_group);
495   if (action == output_action_export_csv)
496     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(csv_rb), TRUE);
497   gtk_tooltips_set_tip (tooltips, csv_rb,
498       "Print output in \"Comma Separated Values\" (CSV) format, "
499       "a text format compatible with OpenOffice and Excel. "
500       "One row for each packet, with its timestamp and size.", NULL);
501   gtk_box_pack_start(GTK_BOX(printer_vb), csv_rb, FALSE, FALSE, 0);
502   /* gtk_widget_show(csv_rb); */
503
504   /* printer table */
505 #ifndef _WIN32
506   printer_tb = gtk_table_new(2, 3, FALSE);
507 #else
508   printer_tb = gtk_table_new(2, 2, FALSE);
509 #endif
510   gtk_box_pack_start(GTK_BOX(printer_vb), printer_tb, FALSE, FALSE, 0);
511   gtk_table_set_row_spacings(GTK_TABLE(printer_tb), 5);
512   gtk_table_set_col_spacings(GTK_TABLE(printer_tb), 5);
513   gtk_widget_show(printer_tb);
514
515
516   /* Output to file button */
517   dest_cb = CHECK_BUTTON_NEW_WITH_MNEMONIC("Output to _file:", accel_group);
518   if (args->to_file)
519     gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(dest_cb), TRUE);
520   gtk_tooltips_set_tip (tooltips, dest_cb, "Output to file instead of printer", NULL);
521   gtk_table_attach_defaults(GTK_TABLE(printer_tb), dest_cb, 0, 1, 0, 1);
522   if(action == output_action_print)
523     gtk_widget_show(dest_cb);
524   
525   /* File text entry */
526   file_te = gtk_entry_new();
527   OBJECT_SET_DATA(dest_cb, PRINT_FILE_TE_KEY, file_te);
528   gtk_tooltips_set_tip (tooltips, file_te, "Enter Output filename", NULL);
529   gtk_entry_set_text(GTK_ENTRY(file_te), args->file);
530   gtk_table_attach_defaults(GTK_TABLE(printer_tb), file_te, 1, 2, 0, 1);
531   gtk_widget_set_sensitive(file_te, args->to_file);
532   gtk_widget_show(file_te);
533   if (args->to_file)
534     gtk_widget_grab_focus(file_te);
535
536   /* "Browse" button */
537   file_bt = BUTTON_NEW_FROM_STOCK(ETHEREAL_STOCK_BROWSE);
538   OBJECT_SET_DATA(dest_cb, PRINT_FILE_BT_KEY, file_bt);
539   OBJECT_SET_DATA(file_bt, PRINT_TE_PTR_KEY, file_te);
540   gtk_tooltips_set_tip (tooltips, file_bt, "Browse output filename in filesystem", NULL);
541   gtk_table_attach_defaults(GTK_TABLE(printer_tb), file_bt, 2, 3, 0, 1);
542   gtk_widget_set_sensitive(file_bt, args->to_file);
543   gtk_widget_show(file_bt);
544
545   /* Command label and text entry */
546 #ifndef _WIN32
547   cmd_lb = gtk_label_new("Print command:");
548   OBJECT_SET_DATA(dest_cb, PRINT_CMD_LB_KEY, cmd_lb);
549   gtk_misc_set_alignment(GTK_MISC(cmd_lb), 1.0, 0.5);
550   gtk_table_attach_defaults(GTK_TABLE(printer_tb), cmd_lb, 0, 1, 1, 2);
551   gtk_widget_set_sensitive(cmd_lb, !args->to_file);
552   if(action == output_action_print)
553     gtk_widget_show(cmd_lb);
554
555   cmd_te = gtk_entry_new();
556   OBJECT_SET_DATA(dest_cb, PRINT_CMD_TE_KEY, cmd_te);
557   gtk_tooltips_set_tip (tooltips, cmd_te, "Enter print command", NULL);
558   gtk_entry_set_text(GTK_ENTRY(cmd_te), args->cmd);
559   gtk_table_attach_defaults(GTK_TABLE(printer_tb), cmd_te, 1, 2, 1, 2);
560   gtk_widget_set_sensitive(cmd_te, !args->to_file);
561   if(action == output_action_print)
562     gtk_widget_show(cmd_te);
563 #endif
564
565   SIGNAL_CONNECT(dest_cb, "toggled", print_cmd_toggle_dest, NULL);
566   SIGNAL_CONNECT(file_bt, "clicked", print_browse_file_cb, file_te);
567
568   if(action == output_action_export_ps) {
569     export_format_lb = gtk_label_new("(PostScript files can be easily converted to PDF files using ghostscript's ps2pdf)");
570     gtk_box_pack_start(GTK_BOX(printer_vb), export_format_lb, FALSE, FALSE, 0);
571     gtk_widget_show(export_format_lb);
572   }
573
574 /*****************************************************/
575
576   /*** hor box for range and format frames ***/
577   packet_hb = gtk_hbox_new(FALSE, 5);
578   gtk_container_add(GTK_CONTAINER(main_vb), packet_hb);
579   gtk_widget_show(packet_hb);
580
581   /*** packet range frame ***/
582   range_fr = gtk_frame_new("Packet Range");
583   gtk_box_pack_start(GTK_BOX(packet_hb), range_fr, FALSE, FALSE, 0);
584   gtk_widget_show(range_fr);
585
586   range_tb = range_new(&args->range
587 #if GTK_MAJOR_VERSION < 2
588   , accel_group
589 #endif
590   );
591   gtk_container_add(GTK_CONTAINER(range_fr), range_tb);
592   gtk_widget_show(range_tb);
593
594 /*****************************************************/
595
596   /*** packet format frame ***/
597   format_fr = gtk_frame_new("Packet Format");
598   gtk_box_pack_start(GTK_BOX(packet_hb), format_fr, TRUE, TRUE, 0);
599   if(   action == output_action_print || 
600         action == output_action_export_text ||
601         action == output_action_export_ps)
602     gtk_widget_show(format_fr);
603   format_vb = gtk_vbox_new(FALSE, 5);
604   gtk_container_border_width(GTK_CONTAINER(format_vb), 5);
605   gtk_container_add(GTK_CONTAINER(format_fr), format_vb);
606   gtk_widget_show(format_vb);
607
608   /* "Print summary line" check button */
609   summary_cb = CHECK_BUTTON_NEW_WITH_MNEMONIC("Packet summary line", accel_group);
610   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(summary_cb), args->print_summary);
611   SIGNAL_CONNECT(summary_cb, "clicked", print_cmd_toggle_detail, main_win);
612   gtk_tooltips_set_tip (tooltips, summary_cb, "Output of a packet summary line, like in the packet list", NULL);
613   gtk_container_add(GTK_CONTAINER(format_vb), summary_cb);
614   gtk_widget_show(summary_cb);
615
616
617   /* "Details" check button */
618   details_cb = CHECK_BUTTON_NEW_WITH_MNEMONIC("Packet details:", accel_group);
619   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(details_cb), args->print_dissections != print_dissections_none);
620   SIGNAL_CONNECT(details_cb, "clicked", print_cmd_toggle_detail, main_win);
621   gtk_tooltips_set_tip (tooltips, details_cb, "Output format of the selected packet details (protocol tree).", NULL);
622   gtk_container_add(GTK_CONTAINER(format_vb), details_cb);
623   gtk_widget_show(details_cb);
624
625   /*** packet details ***/
626   details_hb = gtk_hbox_new(FALSE, 6);
627   gtk_container_border_width(GTK_CONTAINER(details_hb), 0);
628   gtk_container_add(GTK_CONTAINER(format_vb), details_hb);
629   gtk_widget_show(details_hb);
630
631   details_vb = gtk_vbox_new(FALSE, 6);
632   gtk_container_border_width(GTK_CONTAINER(details_vb), 0);
633   gtk_container_add(GTK_CONTAINER(details_hb), details_vb);
634   gtk_widget_show(details_vb);
635
636   details_vb = gtk_vbox_new(FALSE, 6);
637   gtk_container_border_width(GTK_CONTAINER(details_vb), 0);
638   gtk_container_add(GTK_CONTAINER(details_hb), details_vb);
639   gtk_widget_show(details_vb);
640
641   /* "All collapsed"/"As displayed"/"All Expanded" radio buttons */
642   collapse_all_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(NULL, "All co_llapsed", accel_group);
643   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(collapse_all_rb), args->print_dissections == print_dissections_collapsed);
644   gtk_tooltips_set_tip (tooltips, collapse_all_rb, "Output of the packet details tree \"collapsed\"", NULL);
645   gtk_container_add(GTK_CONTAINER(details_vb), collapse_all_rb);
646   gtk_widget_show(collapse_all_rb);
647
648   as_displayed_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(collapse_all_rb, "As displa_yed", accel_group);
649   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(as_displayed_rb), args->print_dissections == print_dissections_as_displayed);
650   gtk_tooltips_set_tip (tooltips, as_displayed_rb, "Output of the packet details tree \"as displayed\"", NULL);
651   gtk_container_add(GTK_CONTAINER(details_vb), as_displayed_rb);
652   gtk_widget_show(as_displayed_rb);
653
654   expand_all_rb = RADIO_BUTTON_NEW_WITH_MNEMONIC(collapse_all_rb, "All e_xpanded", accel_group);
655   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(expand_all_rb), args->print_dissections == print_dissections_expanded);
656   gtk_tooltips_set_tip (tooltips, expand_all_rb, "Output of the packet details tree \"expanded\"", NULL);
657   gtk_container_add(GTK_CONTAINER(details_vb), expand_all_rb);
658   gtk_widget_show(expand_all_rb);
659
660   /* "Print hex" check button. */
661   hex_cb = CHECK_BUTTON_NEW_WITH_MNEMONIC("Packet bytes", accel_group);
662   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(hex_cb), args->print_hex);
663   SIGNAL_CONNECT(hex_cb, "clicked", print_cmd_toggle_detail, main_win);
664   gtk_tooltips_set_tip (tooltips, hex_cb, "Add a hexdump of the packet data", NULL);
665   gtk_container_add(GTK_CONTAINER(format_vb), hex_cb);
666   gtk_widget_show(hex_cb);
667
668   /* seperator */
669   sep = gtk_hseparator_new();
670   gtk_container_add(GTK_CONTAINER(format_vb), sep);
671   gtk_widget_show(sep);
672
673   /* "Each packet on a new page" check button. */
674   formfeed_cb = CHECK_BUTTON_NEW_WITH_MNEMONIC("Each packet on a new page", accel_group);
675   gtk_toggle_button_set_state(GTK_TOGGLE_BUTTON(formfeed_cb), args->print_formfeed);
676   gtk_tooltips_set_tip (tooltips, formfeed_cb, "When checked, a new page will be used for each packet. "
677       "This is done by adding a formfeed (or similar) between the packet outputs.", NULL);
678   gtk_container_add(GTK_CONTAINER(format_vb), formfeed_cb);
679   gtk_widget_show(formfeed_cb);
680
681
682   OBJECT_SET_DATA(main_win, PRINT_ARGS_KEY, args);
683   OBJECT_SET_DATA(main_win, PRINT_SUMMARY_CB_KEY, summary_cb);
684   OBJECT_SET_DATA(main_win, PRINT_DETAILS_CB_KEY, details_cb);
685   OBJECT_SET_DATA(main_win, PRINT_COLLAPSE_ALL_RB_KEY, collapse_all_rb);
686   OBJECT_SET_DATA(main_win, PRINT_AS_DISPLAYED_RB_KEY, as_displayed_rb);
687   OBJECT_SET_DATA(main_win, PRINT_EXPAND_ALL_RB_KEY, expand_all_rb);
688   OBJECT_SET_DATA(main_win, PRINT_HEX_CB_KEY, hex_cb);
689
690 /*****************************************************/
691
692
693   /* Button row */
694   if(topic_available(HELP_PRINT_DIALOG)) {
695     bbox = dlg_button_row_new(action == output_action_print ? GTK_STOCK_PRINT : GTK_STOCK_OK, GTK_STOCK_CANCEL, GTK_STOCK_HELP, NULL);
696   } else {
697     bbox = dlg_button_row_new(action == output_action_print ? GTK_STOCK_PRINT : GTK_STOCK_OK, GTK_STOCK_CANCEL, NULL);
698   }
699   gtk_box_pack_start(GTK_BOX(main_vb), bbox, FALSE, FALSE, 0);
700   gtk_widget_show(bbox);
701
702   ok_bt = OBJECT_GET_DATA(bbox, action == output_action_print ? GTK_STOCK_PRINT : GTK_STOCK_OK);
703
704   OBJECT_SET_DATA(main_win, PRINT_BT_KEY, ok_bt);
705
706   OBJECT_SET_DATA(ok_bt, PRINT_PS_RB_KEY, ps_rb);
707   OBJECT_SET_DATA(ok_bt, PRINT_PDML_RB_KEY, pdml_rb);
708   OBJECT_SET_DATA(ok_bt, PRINT_PSML_RB_KEY, psml_rb);
709   OBJECT_SET_DATA(ok_bt, PRINT_CSV_RB_KEY, csv_rb);
710   OBJECT_SET_DATA(ok_bt, PRINT_DEST_CB_KEY, dest_cb);
711 #ifndef _WIN32
712   OBJECT_SET_DATA(ok_bt, PRINT_CMD_TE_KEY, cmd_te);
713 #endif
714
715   OBJECT_SET_DATA(ok_bt, PRINT_ARGS_KEY, args);
716   OBJECT_SET_DATA(ok_bt, PRINT_FILE_TE_KEY, file_te);
717   OBJECT_SET_DATA(ok_bt, PRINT_SUMMARY_CB_KEY, summary_cb);
718   OBJECT_SET_DATA(ok_bt, PRINT_DETAILS_CB_KEY, details_cb);
719   OBJECT_SET_DATA(ok_bt, PRINT_COLLAPSE_ALL_RB_KEY, collapse_all_rb);
720   OBJECT_SET_DATA(ok_bt, PRINT_AS_DISPLAYED_RB_KEY, as_displayed_rb);
721   OBJECT_SET_DATA(ok_bt, PRINT_EXPAND_ALL_RB_KEY, expand_all_rb);
722   OBJECT_SET_DATA(ok_bt, PRINT_HEX_CB_KEY, hex_cb);
723   OBJECT_SET_DATA(ok_bt, PRINT_FORMFEED_CB_KEY, formfeed_cb);
724   SIGNAL_CONNECT(ok_bt, "clicked", print_ok_cb, main_win);
725   gtk_tooltips_set_tip (tooltips, ok_bt, "Start output", NULL);
726
727   cancel_bt  = OBJECT_GET_DATA(bbox, GTK_STOCK_CANCEL);
728   window_set_cancel_button(main_win, cancel_bt, window_cancel_button_cb);
729   gtk_tooltips_set_tip (tooltips, cancel_bt, "Cancel and exit dialog", NULL);
730
731   if(topic_available(HELP_PRINT_DIALOG)) {
732     help_bt  = OBJECT_GET_DATA(bbox, GTK_STOCK_HELP);
733     SIGNAL_CONNECT(help_bt, "clicked", topic_cb, HELP_PRINT_DIALOG);
734   }
735
736   gtk_widget_grab_default(ok_bt);
737
738   /* Catch the "activate" signal on the "Command" and "File" text entries,
739      so that if the user types Return there, we act as if the "OK" button
740      had been selected, as happens if Return is typed if some widget
741      that *doesn't* handle the Return key has the input focus. */
742 #ifndef _WIN32
743   dlg_set_activate(cmd_te, ok_bt);
744 #endif
745   if(action != output_action_print)
746     dlg_set_activate(file_te, ok_bt);
747
748   SIGNAL_CONNECT(main_win, "delete_event", window_delete_event_cb, NULL);
749
750   gtk_widget_show(main_win);
751   window_present(main_win);
752
753   return main_win;
754 }
755
756
757 /* user changed "print to" destination */
758 static void
759 print_cmd_toggle_dest(GtkWidget *widget, gpointer data _U_)
760 {
761 #ifndef _WIN32
762   GtkWidget     *cmd_lb, *cmd_te;
763 #endif
764   GtkWidget     *file_bt, *file_te;
765   int            to_file;
766
767 #ifndef _WIN32
768   cmd_lb = GTK_WIDGET(OBJECT_GET_DATA(widget, PRINT_CMD_LB_KEY));
769   cmd_te = GTK_WIDGET(OBJECT_GET_DATA(widget, PRINT_CMD_TE_KEY));
770 #endif
771   file_bt = GTK_WIDGET(OBJECT_GET_DATA(widget, PRINT_FILE_BT_KEY));
772   file_te = GTK_WIDGET(OBJECT_GET_DATA(widget, PRINT_FILE_TE_KEY));
773
774   to_file = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
775 #ifndef _WIN32
776   gtk_widget_set_sensitive(cmd_lb, !to_file);
777   gtk_widget_set_sensitive(cmd_te, !to_file);
778 #endif
779   gtk_widget_set_sensitive(file_bt, to_file);
780   gtk_widget_set_sensitive(file_te, to_file);
781 }
782
783
784 /* user changed "packet details" */
785 static void
786 print_cmd_toggle_detail(GtkWidget *widget _U_, gpointer data)
787 {
788   GtkWidget     *print_bt, *summary_cb, *details_cb, *collapse_all_rb, *expand_all_rb, *as_displayed_rb, *hex_cb;
789   gboolean      print_detail;
790
791
792   print_bt = GTK_WIDGET(OBJECT_GET_DATA(data, PRINT_BT_KEY));
793   summary_cb = GTK_WIDGET(OBJECT_GET_DATA(data, PRINT_SUMMARY_CB_KEY));
794   details_cb = GTK_WIDGET(OBJECT_GET_DATA(data, PRINT_DETAILS_CB_KEY));
795   collapse_all_rb = GTK_WIDGET(OBJECT_GET_DATA(data, PRINT_COLLAPSE_ALL_RB_KEY));
796   as_displayed_rb = GTK_WIDGET(OBJECT_GET_DATA(data, PRINT_AS_DISPLAYED_RB_KEY));
797   expand_all_rb = GTK_WIDGET(OBJECT_GET_DATA(data, PRINT_EXPAND_ALL_RB_KEY));
798   hex_cb = GTK_WIDGET(OBJECT_GET_DATA(data, PRINT_HEX_CB_KEY));
799
800   /* is user disabled details, disable the corresponding buttons */
801   print_detail = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (details_cb));
802   gtk_widget_set_sensitive(collapse_all_rb, print_detail);
803   gtk_widget_set_sensitive(as_displayed_rb, print_detail);
804   gtk_widget_set_sensitive(expand_all_rb, print_detail);
805
806   /* if user selected nothing to print at all, disable the "ok" button */
807   gtk_widget_set_sensitive(print_bt, 
808       gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (summary_cb)) ||
809       gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (details_cb)) ||
810       gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (hex_cb)));
811 }
812
813
814 static void
815 print_ok_cb(GtkWidget *ok_bt, gpointer parent_w)
816 {
817   GtkWidget         *button;
818   print_args_t      *args;
819   const gchar       *g_dest;
820   gchar             *f_name;
821   gchar             *dirname;
822   gboolean          export_as_pdml = FALSE, export_as_psml = FALSE;
823   gboolean          export_as_csv = FALSE;
824 #ifdef _WIN32
825   gboolean          win_printer = FALSE;
826 #endif
827   cf_print_status_t status;
828
829   args = (print_args_t *)OBJECT_GET_DATA(ok_bt, PRINT_ARGS_KEY);
830
831   /* Check whether the range is valid. */
832   if (!range_check_validity(&args->range)) {
833     /* The range isn't valid; don't dismiss the print/export dialog box,
834        just leave it around so that the user can, after they
835        dismiss the alert box popped up for the error, try again. */
836     return;
837   }
838
839   button = (GtkWidget *)OBJECT_GET_DATA(ok_bt, PRINT_DEST_CB_KEY);
840   args->to_file = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button));
841
842   if (args->to_file) {
843     g_dest = gtk_entry_get_text(GTK_ENTRY(OBJECT_GET_DATA(ok_bt,
844                                                           PRINT_FILE_TE_KEY)));
845     if (!g_dest[0]) {
846       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
847         "Output to file, but no file specified.");
848       return;
849     }
850     g_free(args->file);
851     args->file = g_strdup(g_dest);
852     /* Save the directory name for future file dialogs. */
853     f_name = g_strdup(g_dest);
854     dirname = get_dirname(f_name);  /* Overwrites f_name */
855     set_last_open_dir(dirname);
856     g_free(f_name);
857   } else {
858 #ifdef _WIN32
859     win_printer = TRUE;
860     /*XXX should use temp file stuff in util routines */
861     g_free(args->file);
862     args->file = g_strdup(tmpnam(NULL));
863     args->to_file = TRUE;
864 #else
865     g_free(args->cmd);
866     args->cmd = g_strdup(gtk_entry_get_text(GTK_ENTRY(OBJECT_GET_DATA(ok_bt,
867       PRINT_CMD_TE_KEY))));
868 #endif
869   }
870
871   args->format = PR_FMT_TEXT;
872   button = (GtkWidget *)OBJECT_GET_DATA(ok_bt, PRINT_PS_RB_KEY);
873   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button)))
874     args->format = PR_FMT_PS;
875   button = (GtkWidget *)OBJECT_GET_DATA(ok_bt, PRINT_PDML_RB_KEY);
876   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button)))
877     export_as_pdml = TRUE;
878   button = (GtkWidget *)OBJECT_GET_DATA(ok_bt, PRINT_PSML_RB_KEY);
879   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button)))
880     export_as_psml = TRUE;
881   button = (GtkWidget *)OBJECT_GET_DATA(ok_bt, PRINT_CSV_RB_KEY);
882   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button)))
883     export_as_csv = TRUE;
884
885   button = (GtkWidget *)OBJECT_GET_DATA(ok_bt, PRINT_SUMMARY_CB_KEY);
886   args->print_summary = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button));
887
888   button = (GtkWidget *)OBJECT_GET_DATA(ok_bt, PRINT_COLLAPSE_ALL_RB_KEY);
889   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button))) {
890     args->print_dissections = print_dissections_collapsed;
891   }
892   button = (GtkWidget *)OBJECT_GET_DATA(ok_bt, PRINT_AS_DISPLAYED_RB_KEY);
893   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button))) {
894     args->print_dissections = print_dissections_as_displayed;
895   }
896   button = (GtkWidget *)OBJECT_GET_DATA(ok_bt, PRINT_EXPAND_ALL_RB_KEY);
897   if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button))) {
898     args->print_dissections = print_dissections_expanded;
899   }
900
901   /* the details setting has priority over the radio buttons */
902   button = (GtkWidget *)OBJECT_GET_DATA(ok_bt, PRINT_DETAILS_CB_KEY);
903   if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button))) {
904     args->print_dissections = print_dissections_none;
905   }
906
907   button = (GtkWidget *)OBJECT_GET_DATA(ok_bt, PRINT_HEX_CB_KEY);
908   args->print_hex = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button));
909
910   button = (GtkWidget *)OBJECT_GET_DATA(ok_bt, PRINT_FORMFEED_CB_KEY);
911   args->print_formfeed = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (button));
912
913
914   window_destroy(GTK_WIDGET(parent_w));
915
916   /* Now print/export the packets */
917   if (export_as_pdml)
918     status = cf_write_pdml_packets(&cfile, args);
919   else if (export_as_psml)
920     status = cf_write_psml_packets(&cfile, args);
921   else if (export_as_csv)
922     status = cf_write_csv_packets(&cfile, args);
923   else {
924     switch (args->format) {
925
926     case PR_FMT_TEXT:
927       if (args->to_file) {
928         args->stream = print_stream_text_new(TRUE, args->file);
929         if (args->stream == NULL) {
930           open_failure_alert_box(args->file, errno, TRUE);
931           return;
932         }
933       } else {
934         args->stream = print_stream_text_new(FALSE, args->cmd);
935         if (args->stream == NULL) {
936           simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
937                         "Couldn't run print command %s.", args->cmd);
938         }
939       }
940       break;
941
942     case PR_FMT_PS:
943       if (args->to_file) {
944         args->stream = print_stream_ps_new(TRUE, args->file);
945         if (args->stream == NULL) {
946           open_failure_alert_box(args->file, errno, TRUE);
947           return;
948         }
949       } else {
950         args->stream = print_stream_ps_new(FALSE, args->cmd);
951         if (args->stream == NULL) {
952           simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
953                         "Couldn't run print command %s.", args->cmd);
954         }
955       }
956       break;
957
958     default:
959       g_assert_not_reached();
960       return;
961     }
962     status = cf_print_packets(&cfile, args);
963   }
964   switch (status) {
965
966   case CF_PRINT_OK:
967     break;
968
969   case CF_PRINT_OPEN_ERROR:
970     if (args->to_file)
971       open_failure_alert_box(args->file, errno, TRUE);
972     else
973       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Couldn't run print command %s.",
974         args->cmd);
975     break;
976
977   case CF_PRINT_WRITE_ERROR:
978     if (args->to_file)
979       write_failure_alert_box(args->file, errno);
980     else
981       simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
982         "Error writing to print command: %s", strerror(errno));
983     break;
984   }
985
986 #ifdef _WIN32
987   if (win_printer) {
988     print_mswin(args->file);
989
990     /* trash temp file */
991     remove(args->file);
992   }
993 #endif
994 }
995
996 static void
997 print_destroy_cb(GtkWidget *win, gpointer user_data)
998 {
999   GtkWidget     *fs;
1000
1001   /* Is there a file selection dialog associated with this
1002      Print File dialog? */
1003   fs = OBJECT_GET_DATA(win, E_FILE_SEL_DIALOG_PTR_KEY);
1004
1005   if (fs != NULL) {
1006     /* Yes.  Destroy it. */
1007     window_destroy(fs);
1008   }
1009
1010   /* Note that we no longer have a "Print" dialog box. */
1011   *((gpointer *) user_data) = NULL;
1012 }