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