Remove unneeded includes from ui folder
[metze/wireshark/wip.git] / ui / gtk / simple_dialog.c
1 /* simple_dialog.c
2  * Simple message dialog box routines.
3  *
4  * Wireshark - Network traffic analyzer
5  * By Gerald Combs <gerald@wireshark.org>
6  * Copyright 1998 Gerald Combs
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22
23 #include "config.h"
24
25 #include <gtk/gtk.h>
26
27 #include "epan/strutil.h"
28
29 #include "simple_dialog.h"
30
31 #include "gtkglobals.h"
32 #include "dlg_utils.h"
33 #include "gui_utils.h"
34 #include "stock_icons.h"
35
36
37 static void simple_dialog_cancel_cb(GtkWidget *, gpointer);
38
39 #define CALLBACK_FCT_KEY    "ESD_Callback_Fct"
40 #define CALLBACK_BTN_KEY    "ESD_Callback_Btn"
41 #define CALLBACK_DATA_KEY   "ESD_Callback_Data"
42 #define CHECK_BUTTON        "ESD_Check_Button"
43
44 /*
45  * Queue for messages requested before we have a main window.
46  */
47 typedef struct {
48   gint  type;
49   gint  btn_mask;
50   char *message;
51 } queued_message_t;
52
53 static GSList *message_queue;
54
55 static GtkWidget *
56 display_simple_dialog(gint type, gint btn_mask, char *message)
57 {
58   GtkWidget   *win, *main_vb, *top_hb, *msg_vb, *type_pm, *msg_label, *ask_cb,
59               *bbox, *ok_bt, *yes_bt, *bt, *save_bt, *dont_save_bt;
60
61   /* Main window */
62   switch (type) {
63   case ESD_TYPE_WARN :
64     type_pm = ws_gtk_image_new_from_stock( GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG);
65     break;
66   case ESD_TYPE_CONFIRMATION:
67     type_pm = ws_gtk_image_new_from_stock( GTK_STOCK_DIALOG_WARNING, GTK_ICON_SIZE_DIALOG);
68     break;
69   case ESD_TYPE_ERROR:
70     type_pm = ws_gtk_image_new_from_stock( GTK_STOCK_DIALOG_ERROR, GTK_ICON_SIZE_DIALOG);
71     break;
72   case ESD_TYPE_STOP :
73     type_pm = ws_gtk_image_new_from_stock( GTK_STOCK_STOP, GTK_ICON_SIZE_DIALOG);
74     break;
75   case ESD_TYPE_INFO :
76   default :
77     type_pm = ws_gtk_image_new_from_stock( GTK_STOCK_DIALOG_INFO, GTK_ICON_SIZE_DIALOG);
78     break;
79   }
80
81   /*
82    * The GNOME HIG:
83    *
84    *    http://developer.gnome.org/projects/gup/hig/1.0/windows.html#alert-windows
85    *
86    * says that the title should be empty for alert boxes, so there's "less
87    * visual noise and confounding text."
88    *
89    * The Windows HIG:
90    *
91    *    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwue/html/ch09f.asp
92    *
93    * says it should
94    *
95    *    ...appropriately identify the source of the message -- usually
96    *    the name of the object.  For example, if the message results
97    *    from editing a document, the title text is the name of the
98    *    document, optionally followed by the application name.  If the
99    *    message results from a non-document object, then use the
100    *    application name."
101    *
102    * and notes that the title is important "because message boxes might
103    * not always the the result of current user interaction" (e.g., some
104    * app might randomly pop something up, e.g. some browser letting you
105    * know that it couldn't fetch something because of a timeout).
106    *
107    * It also says not to use "warning" or "caution", as there's already
108    * an icon that tells you what type of alert it is, and that you
109    * shouldn't say "error", as that provides no useful information.
110    *
111    * So we give it a title on Win32, and don't give it one on UN*X.
112    * For now, we give it a Win32 title of just "Wireshark"; we should
113    * arguably take an argument for the title.
114    */
115   if(btn_mask == ESD_BTN_NONE) {
116     win = splash_window_new();
117   } else {
118 #ifdef _WIN32
119     win = dlg_window_new("Wireshark");
120 #else
121     win = dlg_window_new("");
122 #endif
123   }
124
125   gtk_window_set_modal(GTK_WINDOW(win), TRUE);
126   gtk_container_set_border_width(GTK_CONTAINER(win), 6);
127
128   /* Container for our rows */
129   main_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 12, FALSE);
130   gtk_container_add(GTK_CONTAINER(win), main_vb);
131   gtk_widget_show(main_vb);
132
133   /* Top row: Icon and message text */
134   top_hb = ws_gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12, FALSE);
135   gtk_container_set_border_width(GTK_CONTAINER(main_vb), 6);
136   gtk_box_pack_start(GTK_BOX(main_vb), top_hb, TRUE, TRUE, 0);
137   gtk_widget_show(top_hb);
138
139   gtk_misc_set_alignment (GTK_MISC (type_pm), 0.5f, 0.0f);
140   gtk_box_pack_start(GTK_BOX(top_hb), type_pm, TRUE, TRUE, 0);
141   gtk_widget_show(type_pm);
142
143   /* column for message and optional check button */
144   msg_vb = ws_gtk_box_new(GTK_ORIENTATION_VERTICAL, 6, FALSE);
145   gtk_box_set_spacing(GTK_BOX(msg_vb), 24);
146   gtk_box_pack_start(GTK_BOX(top_hb), msg_vb, TRUE, TRUE, 0);
147   gtk_widget_show(msg_vb);
148
149   /* message */
150   msg_label = gtk_label_new(message);
151
152   gtk_label_set_markup(GTK_LABEL(msg_label), message);
153   gtk_label_set_selectable(GTK_LABEL(msg_label), TRUE);
154   g_object_set(gtk_widget_get_settings(msg_label),
155     "gtk-label-select-on-focus", FALSE, NULL);
156
157   gtk_label_set_justify(GTK_LABEL(msg_label), GTK_JUSTIFY_FILL);
158   gtk_misc_set_alignment (GTK_MISC (type_pm), 0.5f, 0.0f);
159   gtk_box_pack_start(GTK_BOX(msg_vb), msg_label, TRUE, TRUE, 0);
160   gtk_label_set_line_wrap(GTK_LABEL(msg_label), TRUE);
161   gtk_widget_show(msg_label);
162
163   if(btn_mask == ESD_BTN_NONE) {
164     gtk_widget_show(win);
165     return win;
166   }
167
168   /* optional check button */
169   ask_cb = gtk_check_button_new_with_label("replace with text...");
170   gtk_box_pack_start(GTK_BOX(msg_vb), ask_cb, TRUE, TRUE, 0);
171   g_object_set_data(G_OBJECT(win), CHECK_BUTTON, ask_cb);
172
173   /* Button row */
174   switch(btn_mask) {
175   case(ESD_BTN_OK):
176     bbox = dlg_button_row_new(GTK_STOCK_OK, NULL);
177     break;
178   case(ESD_BTN_OK | ESD_BTN_CANCEL):
179     bbox = dlg_button_row_new(GTK_STOCK_OK, GTK_STOCK_CANCEL, NULL);
180     break;
181   case(ESD_BTN_CLEAR | ESD_BTN_CANCEL):
182     bbox = dlg_button_row_new(GTK_STOCK_CLEAR, GTK_STOCK_CANCEL, NULL);
183     break;
184   case(ESD_BTNS_YES_NO_CANCEL):
185     bbox = dlg_button_row_new(GTK_STOCK_YES, GTK_STOCK_NO, GTK_STOCK_CANCEL, NULL);
186     break;
187   case(ESD_BTNS_SAVE_DONTSAVE):
188     bbox = dlg_button_row_new(GTK_STOCK_SAVE, WIRESHARK_STOCK_DONT_SAVE, NULL);
189     break;
190   case(ESD_BTNS_SAVE_DONTSAVE_CANCEL):
191     bbox = dlg_button_row_new(GTK_STOCK_SAVE, WIRESHARK_STOCK_DONT_SAVE, GTK_STOCK_CANCEL, NULL);
192     break;
193   case(ESD_BTNS_SAVE_QUIT_DONTSAVE_CANCEL):
194     bbox = dlg_button_row_new(GTK_STOCK_SAVE, WIRESHARK_STOCK_QUIT_DONT_SAVE, GTK_STOCK_CANCEL, NULL);
195     break;
196   case (ESD_BTNS_QUIT_DONTSAVE_CANCEL):
197     bbox = dlg_button_row_new(WIRESHARK_STOCK_QUIT_DONT_SAVE, GTK_STOCK_CANCEL, NULL);
198     break;
199   case(ESD_BTNS_YES_NO):
200     bbox = dlg_button_row_new(GTK_STOCK_YES, GTK_STOCK_NO, NULL);
201     break;
202   default:
203     g_assert_not_reached();
204     bbox = NULL;
205     break;
206   }
207   gtk_box_pack_start(GTK_BOX(main_vb), bbox, TRUE, TRUE, 0);
208   gtk_widget_show(bbox);
209
210   ok_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_OK);
211   if(ok_bt) {
212     g_object_set_data(G_OBJECT(ok_bt), CALLBACK_BTN_KEY, GINT_TO_POINTER(ESD_BTN_OK));
213     g_signal_connect(ok_bt, "clicked", G_CALLBACK(simple_dialog_cancel_cb), win);
214   }
215
216   save_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_SAVE);
217   if (save_bt) {
218     g_object_set_data(G_OBJECT(save_bt), CALLBACK_BTN_KEY, GINT_TO_POINTER(ESD_BTN_SAVE));
219     g_signal_connect(save_bt, "clicked", G_CALLBACK(simple_dialog_cancel_cb), win);
220   }
221
222   dont_save_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_DONT_SAVE);
223   if (dont_save_bt) {
224     g_object_set_data(G_OBJECT(dont_save_bt), CALLBACK_BTN_KEY, GINT_TO_POINTER(ESD_BTN_DONT_SAVE));
225     g_signal_connect(dont_save_bt, "clicked", G_CALLBACK(simple_dialog_cancel_cb), win);
226   }
227
228   dont_save_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), WIRESHARK_STOCK_QUIT_DONT_SAVE);
229   if (dont_save_bt) {
230     g_object_set_data(G_OBJECT(dont_save_bt), CALLBACK_BTN_KEY, GINT_TO_POINTER(ESD_BTN_QUIT_DONT_SAVE));
231     g_signal_connect(dont_save_bt, "clicked", G_CALLBACK(simple_dialog_cancel_cb), win);
232   }
233   bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLEAR);
234   if(bt) {
235     g_object_set_data(G_OBJECT(bt), CALLBACK_BTN_KEY, GINT_TO_POINTER(ESD_BTN_CLEAR));
236     g_signal_connect(bt, "clicked", G_CALLBACK(simple_dialog_cancel_cb), win);
237   }
238
239   yes_bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_YES);
240   if(yes_bt) {
241     g_object_set_data(G_OBJECT(yes_bt), CALLBACK_BTN_KEY, GINT_TO_POINTER(ESD_BTN_YES));
242     g_signal_connect(yes_bt, "clicked", G_CALLBACK(simple_dialog_cancel_cb), win);
243   }
244
245   bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_NO);
246   if(bt) {
247     g_object_set_data(G_OBJECT(bt), CALLBACK_BTN_KEY, GINT_TO_POINTER(ESD_BTN_NO));
248     g_signal_connect(bt, "clicked", G_CALLBACK(simple_dialog_cancel_cb), win);
249   }
250
251   bt = (GtkWidget *)g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CANCEL);
252   if(bt) {
253     g_object_set_data(G_OBJECT(bt), CALLBACK_BTN_KEY, GINT_TO_POINTER(ESD_BTN_CANCEL));
254     window_set_cancel_button(win, bt, simple_dialog_cancel_cb);
255   }
256
257   if(!bt) {
258     if(yes_bt) {
259       window_set_cancel_button(win, yes_bt, simple_dialog_cancel_cb);
260     } else if (ok_bt) {
261       window_set_cancel_button(win, ok_bt, simple_dialog_cancel_cb);
262     }
263   }
264
265   dlg_button_focus_nth(bbox, 0);
266
267   gtk_widget_show(win);
268
269   return win;
270 }
271
272 void
273 display_queued_messages(void)
274 {
275   queued_message_t *queued_message;
276
277   while (message_queue != NULL) {
278     queued_message = (queued_message_t *)message_queue->data;
279     message_queue = g_slist_remove(message_queue, queued_message);
280
281     display_simple_dialog(queued_message->type, queued_message->btn_mask,
282                           queued_message->message);
283
284     g_free(queued_message->message);
285     g_free(queued_message);
286   }
287 }
288
289 /* Simple dialog function - Displays a dialog box with the supplied message
290  * text.
291  *
292  * Args:
293  * type       : One of ESD_TYPE_*.
294  * btn_mask   : The value passed in determines which buttons are displayed.
295  * msg_format : Sprintf-style format of the text displayed in the dialog.
296  * ...        : Argument list for msg_format
297  */
298
299 static gpointer
300 vsimple_dialog(ESD_TYPE_E type, gint btn_mask, const gchar *msg_format, va_list ap)
301 {
302   gchar            *message;
303   queued_message_t *queued_message;
304   GtkWidget        *win;
305   GdkWindowState    state = (GdkWindowState)0;
306
307   /* Format the message. */
308   message = g_strdup_vprintf(msg_format, ap);
309
310   if (top_level != NULL) {
311     state = gdk_window_get_state(gtk_widget_get_window(top_level));
312   }
313
314   /* If we don't yet have a main window or it's iconified or hidden (i.e. not
315      yet ready, don't show the dialog. If showing up a dialog, while main
316      window is iconified, program will become unresponsive! */
317   if ((top_level == NULL) || state & GDK_WINDOW_STATE_ICONIFIED
318           || state & GDK_WINDOW_STATE_WITHDRAWN) {
319
320     queued_message = (queued_message_t *)g_malloc(sizeof (queued_message_t));
321     queued_message->type = type;
322     queued_message->btn_mask = btn_mask;
323     queued_message->message = message;
324     message_queue = g_slist_append(message_queue, queued_message);
325     return NULL;
326   }
327
328   /*
329    * Do we have any queued up messages?  If so, pop them up.
330    */
331   display_queued_messages();
332
333   win = display_simple_dialog(type, btn_mask, message);
334
335   g_free(message);
336
337   return win;
338 }
339
340 gpointer
341 simple_dialog(ESD_TYPE_E type, gint btn_mask, const gchar *msg_format, ...)
342 {
343   va_list  ap;
344   gpointer ret;
345
346   va_start(ap, msg_format);
347   ret = vsimple_dialog(type, btn_mask, msg_format, ap);
348   va_end(ap);
349   return ret;
350 }
351
352 static void
353 simple_dialog_cancel_cb(GtkWidget *w, gpointer win) {
354   gint               button       = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(w), CALLBACK_BTN_KEY));
355   simple_dialog_cb_t callback_fct = (simple_dialog_cb_t)g_object_get_data(G_OBJECT(win), CALLBACK_FCT_KEY);
356   gpointer           data         = g_object_get_data(G_OBJECT(win), CALLBACK_DATA_KEY);
357
358   if (callback_fct)
359     (callback_fct) (win, button, data);
360
361     window_destroy(GTK_WIDGET(win));
362 }
363
364 void
365 simple_dialog_close(gpointer dialog)
366 {
367     window_destroy(GTK_WIDGET(dialog));
368 }
369
370 void
371 simple_dialog_set_cb(gpointer dialog, simple_dialog_cb_t callback_fct, gpointer data)
372 {
373
374     g_object_set_data(G_OBJECT(GTK_WIDGET(dialog)), CALLBACK_FCT_KEY, callback_fct);
375     g_object_set_data(G_OBJECT(GTK_WIDGET(dialog)), CALLBACK_DATA_KEY, data);
376 }
377
378 void
379 simple_dialog_check_set(gpointer dialog, const gchar *text) {
380     GtkWidget *ask_cb = (GtkWidget *)g_object_get_data(G_OBJECT(dialog), CHECK_BUTTON);
381
382     gtk_button_set_label(GTK_BUTTON(ask_cb), text);
383     gtk_widget_show(ask_cb);
384 }
385
386 gboolean
387 simple_dialog_check_get(gpointer dialog) {
388     GtkWidget *ask_cb = (GtkWidget *)g_object_get_data(G_OBJECT(GTK_WIDGET(dialog)), CHECK_BUTTON);
389
390     return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(ask_cb));
391 }
392
393 const char *
394 simple_dialog_primary_start(void) {
395     return "<span weight=\"bold\" size=\"larger\">";
396 }
397
398 const char *
399 simple_dialog_primary_end(void) {
400     return "</span>";
401 }
402
403 char *
404 simple_dialog_format_message(const char *msg)
405 {
406   char *str;
407
408   if (msg) {
409     str = xml_escape(msg);
410   } else {
411     str = NULL;
412   }
413   return str;
414 }
415
416 static void
417 do_simple_message_box(ESD_TYPE_E type, gboolean *notagain,
418                       const char *secondary_msg, const char *msg_format,
419                       va_list ap)
420 {
421   GtkMessageType  gtk_message_type;
422   gchar          *message;
423   GtkWidget      *msg_dialog;
424   GtkWidget      *checkbox = NULL;
425
426   if (notagain != NULL) {
427     if (*notagain) {
428       /*
429        * The user had checked the "Don't show this message again" checkbox
430        * in the past; don't bother showing it.
431        */
432       return;
433     }
434   }
435
436   switch (type) {
437
438   case ESD_TYPE_INFO:
439     gtk_message_type = GTK_MESSAGE_INFO;
440     break;
441
442   case ESD_TYPE_WARN:
443     gtk_message_type = GTK_MESSAGE_WARNING;
444     break;
445
446   case ESD_TYPE_ERROR:
447     gtk_message_type = GTK_MESSAGE_ERROR;
448     break;
449
450   default:
451     g_assert_not_reached();
452     gtk_message_type = GTK_MESSAGE_INFO;
453     break;
454   }
455
456   /* Format the message. */
457   message = g_strdup_vprintf(msg_format, ap);
458   msg_dialog = gtk_message_dialog_new(GTK_WINDOW(top_level),
459                                       (GtkDialogFlags)(GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT),
460                                       gtk_message_type,
461                                       GTK_BUTTONS_OK,
462                                       "%s", message);
463   g_free(message);
464   if (secondary_msg != NULL)
465     gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(msg_dialog),
466                                              "%s", secondary_msg);
467
468   if (notagain != NULL) {
469     checkbox = gtk_check_button_new_with_label("Don't show this message again.");
470     gtk_container_set_border_width(GTK_CONTAINER(checkbox), 12);
471     gtk_box_pack_start(GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(msg_dialog))),
472                        checkbox, TRUE, TRUE, 0);
473     gtk_widget_show(checkbox);
474   }
475
476   gtk_dialog_run(GTK_DIALOG(msg_dialog));
477   if (notagain != NULL) {
478     /*
479      * OK, did they check the checkbox?
480      */
481     *notagain = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbox));
482   }
483   gtk_widget_destroy(msg_dialog);
484 }
485
486 /*
487  * Alert box, with optional "don't show this message again" variable
488  * and checkbox, and optional secondary text.
489  */
490 void
491 simple_message_box(ESD_TYPE_E type, gboolean *notagain,
492                    const char *secondary_msg, const char *msg_format, ...)
493 {
494   va_list ap;
495
496   va_start(ap, msg_format);
497   do_simple_message_box(type, notagain, secondary_msg, msg_format, ap);
498   va_end(ap);
499 }
500
501 /*
502  * Error alert box, taking a format and a va_list argument.
503  */
504 void
505 vsimple_error_message_box(const char *msg_format, va_list ap)
506 {
507   do_simple_message_box(ESD_TYPE_ERROR, NULL, NULL, msg_format, ap);
508 }
509
510 /*
511  * Error alert box, taking a format and a list of arguments.
512  */
513 void
514 simple_error_message_box(const char *msg_format, ...)
515 {
516   va_list ap;
517
518   va_start(ap, msg_format);
519   do_simple_message_box(ESD_TYPE_ERROR, NULL, NULL, msg_format, ap);
520   va_end(ap);
521 }