On Windows enable threads everywhere instead of just in dumpcap. If
[obnox/wireshark/wip.git] / gtk / main_welcome.c
1 /* main_welcome.c
2  *
3  * $Id$
4  *
5  * Wireshark - Network traffic analyzer
6  * By Gerald Combs <gerald@wireshark.org>
7  * Copyright 1998 Gerald Combs
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License
11  * as published by the Free Software Foundation; either version 2
12  * of the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #ifdef HAVE_SYS_TYPES_H
29 #include <sys/types.h>
30 #endif
31 #include <time.h>
32
33 #include <gtk/gtk.h>
34
35 #include <epan/prefs.h>
36
37 #include "../color.h"
38 #ifdef HAVE_LIBPCAP
39 #include "capture.h"
40 #include "capture-pcap-util.h"
41 #include "capture_opts.h"
42 #include "capture_ui_utils.h"
43 #endif
44 #include "simple_dialog.h"
45 #include <wsutil/file_util.h>
46
47 #include "gtk/gui_utils.h"
48 #include "gtk/color_utils.h"
49 #include "gtk/recent.h"
50 #include "gtk/gtkglobals.h"
51 #include "gtk/main.h"
52 #include "gtk/menus.h"
53 #include "gtk/main_welcome.h"
54 #include "gtk/help_dlg.h"
55 #include "gtk/capture_file_dlg.h"
56 #include "gtk/stock_icons.h"
57 #include "gtk/utf8_entities.h"
58 #ifdef HAVE_LIBPCAP
59 #include "gtk/capture_dlg.h"
60 #include "gtk/capture_if_dlg.h"
61 #include "gtk/capture_globals.h"
62 #endif
63 #include "../image/wssplash-dev.xpm"
64 #include "../version_info.h"
65
66 #ifdef _WIN32
67 #include <tchar.h>
68 #include <windows.h>
69 #endif
70
71 #ifdef HAVE_AIRPCAP
72 #include "airpcap.h"
73 #include "airpcap_loader.h"
74 #include "airpcap_gui_utils.h"
75 #include "../image/toolbar/capture_airpcap_16.xpm"
76 #endif
77
78 /* XXX */
79 extern gint if_list_comparator_alph (const void *first_arg, const void *second_arg);
80
81 static GtkWidget *welcome_hb = NULL;
82 static GtkWidget *header_lb = NULL;
83 /* Foreground colors are set using Pango markup */
84 static GdkColor welcome_bg = { 0, 0xe6e6, 0xe6e6, 0xe6e6 };
85 static GdkColor header_bar_bg = { 0, 0x1818, 0x5c5c, 0xcaca };
86 static GdkColor topic_header_bg = { 0, 0x0101, 0x3939, 0xbebe };
87 static GdkColor topic_content_bg = { 0, 0xffff, 0xffff, 0xffff };
88 static GdkColor topic_item_idle_bg;
89 static GdkColor topic_item_entered_bg = { 0, 0xd3d3, 0xd8d8, 0xdada };
90
91 static GtkWidget *welcome_file_panel_vb = NULL;
92 #ifdef HAVE_LIBPCAP
93 static GtkWidget *welcome_if_panel_vb = NULL;
94 static GtkWidget *if_view = NULL;
95 #endif
96
97 static GSList *status_messages = NULL;
98
99 static GMutex *recent_mtx = NULL;
100
101 /* The "scroll box dynamic" is a (complicated) pseudo widget to */
102 /* place a vertically list of widgets in (currently the interfaces and recent files). */
103 /* Once this list get's higher than a specified amount, */
104 /* it is moved into a scrolled_window. */
105 /* This is all complicated, the scrolled window is a bit ugly, */
106 /* the sizes might not be the same on all systems, ... */
107 /* ... but that's the best what we currently have */
108 #define SCROLL_BOX_CHILD_BOX        "ScrollBoxDynamic_ChildBox"
109 #define SCROLL_BOX_MAX_CHILDS       "ScrollBoxDynamic_MaxChilds"
110 #define SCROLL_BOX_SCROLLW_Y_SIZE   "ScrollBoxDynamic_Scrollw_Y_Size"
111 #define SCROLL_BOX_SCROLLW          "ScrollBoxDynamic_Scrollw"
112 #define TREE_VIEW_INTERFACES        "TreeViewInterfaces"
113
114 static GtkWidget *
115 scroll_box_dynamic_new(GtkWidget *child_box, guint max_childs, guint scrollw_y_size) {
116     GtkWidget * parent_box;
117
118
119     parent_box = gtk_vbox_new(FALSE, 0);
120     gtk_box_pack_start(GTK_BOX(parent_box), GTK_WIDGET(child_box), TRUE, TRUE, 0);
121     g_object_set_data(G_OBJECT(parent_box), SCROLL_BOX_CHILD_BOX, child_box);
122     g_object_set_data(G_OBJECT(parent_box), SCROLL_BOX_MAX_CHILDS, GINT_TO_POINTER(max_childs));
123     g_object_set_data(G_OBJECT(parent_box), SCROLL_BOX_SCROLLW_Y_SIZE, GINT_TO_POINTER(scrollw_y_size));
124     gtk_widget_show_all(parent_box);
125
126     return parent_box;
127 }
128
129
130 static GtkWidget *
131 scroll_box_dynamic_add(GtkWidget *parent_box)
132 {
133     GtkWidget *child_box;
134     GtkWidget *scrollw;
135     guint max_cnt;
136     guint curr_cnt;
137     guint scrollw_y_size;
138     GList *childs;
139
140     child_box = g_object_get_data(G_OBJECT(parent_box), SCROLL_BOX_CHILD_BOX);
141     max_cnt = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(parent_box), SCROLL_BOX_MAX_CHILDS));
142
143     /* get the current number of children */
144     childs = gtk_container_get_children(GTK_CONTAINER(child_box));
145     curr_cnt = g_list_length(childs);
146     g_list_free(childs);
147
148     /* have we just reached the max? */
149     if(curr_cnt == max_cnt) {
150         /* create the scrolled window */
151         /* XXX - there's no way to get rid of the shadow frame - except for creating an own widget :-( */
152         scrollw = scrolled_window_new(NULL, NULL);
153         scrollw_y_size = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(parent_box), SCROLL_BOX_SCROLLW_Y_SIZE));
154         gtk_widget_set_size_request(scrollw, -1, scrollw_y_size);
155
156         g_object_set_data(G_OBJECT(parent_box), SCROLL_BOX_SCROLLW, scrollw);
157         gtk_box_pack_start(GTK_BOX(parent_box), scrollw, TRUE, TRUE, 0);
158
159         /* move child_box from parent_box into scrolled window */
160         g_object_ref(child_box);
161         gtk_container_remove(GTK_CONTAINER(parent_box), child_box);
162         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(scrollw),
163                                               child_box);
164         gtk_widget_show_all(scrollw);
165     }
166
167     return child_box;
168 }
169
170
171 static GtkWidget *
172 scroll_box_dynamic_reset(GtkWidget *parent_box)
173 {
174     GtkWidget *child_box, *scrollw;
175
176
177     child_box = g_object_get_data(G_OBJECT(parent_box), SCROLL_BOX_CHILD_BOX);
178     scrollw = g_object_get_data(G_OBJECT(parent_box), SCROLL_BOX_SCROLLW);
179
180     if(scrollw != NULL) {
181         /* move the child_box back from scrolled window into the parent_box */
182         g_object_ref(child_box);
183         gtk_container_remove(GTK_CONTAINER(parent_box), scrollw);
184         g_object_set_data(G_OBJECT(parent_box), SCROLL_BOX_SCROLLW, NULL);
185         gtk_box_pack_start(GTK_BOX(parent_box), child_box, TRUE, TRUE, 0);
186     }
187
188     return child_box;
189 }
190
191
192 /* mouse entered this widget - change background color */
193 static gboolean
194 welcome_item_enter_cb(GtkWidget *eb, GdkEventCrossing *event _U_, gpointer user_data _U_)
195 {
196     gtk_widget_modify_bg(eb, GTK_STATE_NORMAL, &topic_item_entered_bg);
197
198     return FALSE;
199 }
200
201
202 /* mouse has left this widget - change background color  */
203 static gboolean
204 welcome_item_leave_cb(GtkWidget *eb, GdkEventCrossing *event _U_, gpointer user_data _U_)
205 {
206     gtk_widget_modify_bg(eb, GTK_STATE_NORMAL, &topic_item_idle_bg);
207
208     return FALSE;
209 }
210
211
212 typedef gboolean (*welcome_button_callback_t)  (GtkWidget      *widget,
213                                                 GdkEventButton *event,
214                                                 gpointer        user_data);
215
216 /* create a "button widget" */
217 static GtkWidget *
218 welcome_button(const gchar *stock_item,
219                const gchar *title, const gchar *subtitle, const gchar *tooltip,
220                welcome_button_callback_t welcome_button_callback, gpointer welcome_button_callback_data)
221 {
222     GtkWidget *eb, *w, *item_hb, *text_vb;
223     gchar *formatted_text;
224
225     item_hb = gtk_hbox_new(FALSE, 1);
226
227     /* event box (for background color and events) */
228     eb = gtk_event_box_new();
229     gtk_container_add(GTK_CONTAINER(eb), item_hb);
230     gtk_widget_modify_bg(eb, GTK_STATE_NORMAL, &topic_item_idle_bg);
231     if(tooltip != NULL) {
232                 gtk_widget_set_tooltip_text(eb, tooltip);
233     }
234
235     g_signal_connect(eb, "enter-notify-event", G_CALLBACK(welcome_item_enter_cb), NULL);
236     g_signal_connect(eb, "leave-notify-event", G_CALLBACK(welcome_item_leave_cb), NULL);
237     g_signal_connect(eb, "button-release-event", G_CALLBACK(welcome_button_callback), welcome_button_callback_data);
238
239     /* icon */
240     w = gtk_image_new_from_stock(stock_item, GTK_ICON_SIZE_LARGE_TOOLBAR);
241     gtk_box_pack_start(GTK_BOX(item_hb), w, FALSE, FALSE, 5);
242
243     text_vb = gtk_vbox_new(FALSE, 3);
244
245     /* title */
246     w = gtk_label_new(title);
247     gtk_misc_set_alignment (GTK_MISC(w), 0.0f, 0.5f);
248     formatted_text = g_strdup_printf("<span weight=\"bold\" size=\"x-large\" foreground=\"black\">%s</span>", title);
249     gtk_label_set_markup(GTK_LABEL(w), formatted_text);
250     g_free(formatted_text);
251     gtk_box_pack_start(GTK_BOX(text_vb), w, FALSE, FALSE, 1);
252
253     /* subtitle */
254     w = gtk_label_new(subtitle);
255     gtk_misc_set_alignment (GTK_MISC(w), 0.0f, 0.5f);
256     formatted_text = g_strdup_printf("<span size=\"small\" foreground=\"black\">%s</span>", subtitle);
257     gtk_label_set_markup(GTK_LABEL(w), formatted_text);
258     g_free(formatted_text);
259     gtk_box_pack_start(GTK_BOX(text_vb), w, FALSE, FALSE, 1);
260
261     gtk_box_pack_start(GTK_BOX(item_hb), text_vb, TRUE, TRUE, 5);
262
263     return eb;
264 }
265
266
267 /* Hack to handle welcome-button "button-release-event" callback   */
268 /*  1. Dispatch to desired actual callback                         */
269 /*  2. Return TRUE for the event callback.                         */
270 /* user_data: actual (no arg) callback fcn to be invoked.          */
271 static gboolean
272 welcome_button_callback_helper(GtkWidget *w, GdkEventButton *event _U_, gpointer user_data)
273 {
274     void (*funct)(GtkWidget *, gpointer) = user_data;
275     (*funct)(w, NULL);
276     return TRUE;
277 }
278
279
280 void
281 welcome_header_set_message(gchar *msg) {
282     GString *message;
283     time_t secs = time(NULL);
284     struct tm *now = localtime(&secs);
285
286     message = g_string_new("<span weight=\"bold\" size=\"x-large\" foreground=\"white\">");
287
288     if (msg) {
289         g_string_append(message, msg);
290     } else { /* Use our default header */
291         if ((now->tm_mon == 3 && now->tm_mday == 1) || (now->tm_mon == 6 && now->tm_mday == 14)) {
292             g_string_append(message, "Sniffing the glue that holds the Internet together");
293         } else {
294             g_string_append(message, prefs.gui_start_title);
295         }
296
297         if (prefs.gui_version_in_start_page) {
298             g_string_append_printf(message, "</span>\n<span size=\"large\" foreground=\"white\">Version " VERSION "%s",
299                                    wireshark_svnversion);
300         }
301     }
302
303     g_string_append(message, "</span>");
304
305     gtk_label_set_markup(GTK_LABEL(header_lb), message->str);
306     g_string_free(message, TRUE);
307 }
308
309
310 /* create the banner "above our heads" */
311 static GtkWidget *
312 welcome_header_new(void)
313 {
314     GtkWidget *item_vb;
315     GtkWidget *item_hb;
316     GtkWidget *eb;
317     GtkWidget *icon;
318
319     item_vb = gtk_vbox_new(FALSE, 0);
320
321     /* colorize vbox */
322     eb = gtk_event_box_new();
323     gtk_container_add(GTK_CONTAINER(eb), item_vb);
324     gtk_widget_modify_bg(eb, GTK_STATE_NORMAL, &header_bar_bg);
325
326     item_hb = gtk_hbox_new(FALSE, 0);
327     gtk_box_pack_start(GTK_BOX(item_vb), item_hb, FALSE, FALSE, 10);
328
329     icon = xpm_to_widget_from_parent(top_level, wssplash_xpm);
330     gtk_box_pack_start(GTK_BOX(item_hb), icon, FALSE, FALSE, 10);
331
332     header_lb = gtk_label_new(NULL);
333     welcome_header_set_message(NULL);
334     gtk_label_set_selectable(GTK_LABEL(header_lb), TRUE);
335     gtk_misc_set_alignment(GTK_MISC(header_lb), 0.0f, 0.5f);
336     gtk_box_pack_start(GTK_BOX(item_hb), header_lb, TRUE, TRUE, 5);
337
338     gtk_widget_show_all(eb);
339
340     return eb;
341 }
342
343
344 void
345 welcome_header_push_msg(const gchar *msg) {
346     gchar *msg_copy = g_strdup(msg);
347
348     status_messages = g_slist_append(status_messages, msg_copy);
349
350     welcome_header_set_message(msg_copy);
351
352     gtk_widget_hide(welcome_hb);
353 }
354
355
356 void
357 welcome_header_pop_msg(void) {
358     gchar *msg = NULL;
359
360     if (status_messages) {
361         g_free(status_messages->data);
362         status_messages = g_slist_delete_link(status_messages, status_messages);
363     }
364
365     if (status_messages) {
366         msg = status_messages->data;
367     }
368
369     welcome_header_set_message(msg);
370
371     if (!status_messages) {
372         gtk_widget_show(welcome_hb);
373     }
374 }
375
376
377 /* create a "topic header widget" */
378 static GtkWidget *
379 welcome_topic_header_new(const char *header)
380 {
381     GtkWidget *w;
382     GtkWidget *eb;
383     gchar *formatted_message;
384
385
386     w = gtk_label_new(header);
387     formatted_message = g_strdup_printf("<span weight=\"bold\" size=\"x-large\" foreground=\"white\">%s</span>", header);
388     gtk_label_set_markup(GTK_LABEL(w), formatted_message);
389     g_free(formatted_message);
390
391     /* colorize vbox */
392     eb = gtk_event_box_new();
393     gtk_container_add(GTK_CONTAINER(eb), w);
394     gtk_widget_modify_bg(eb, GTK_STATE_NORMAL, &topic_header_bg);
395
396     return eb;
397 }
398
399
400 /* create a "topic widget" */
401 static GtkWidget *
402 welcome_topic_new(const char *header, GtkWidget **to_fill)
403 {
404     GtkWidget *topic_vb;
405     GtkWidget *layout_vb;
406     GtkWidget *topic_eb;
407     GtkWidget *topic_header;
408
409
410     topic_vb = gtk_vbox_new(FALSE, 0);
411
412     topic_header = welcome_topic_header_new(header);
413     gtk_box_pack_start(GTK_BOX(topic_vb), topic_header, FALSE, FALSE, 0);
414
415     layout_vb = gtk_vbox_new(FALSE, 5);
416     gtk_container_set_border_width(GTK_CONTAINER(layout_vb), 10);
417     gtk_box_pack_start(GTK_BOX(topic_vb), layout_vb, FALSE, FALSE, 0);
418
419     /* colorize vbox (we need an event box for this!) */
420     topic_eb = gtk_event_box_new();
421     gtk_container_add(GTK_CONTAINER(topic_eb), topic_vb);
422     gtk_widget_modify_bg(topic_eb, GTK_STATE_NORMAL, &topic_content_bg);
423     *to_fill = layout_vb;
424
425     return topic_eb;
426 }
427
428
429 /* a file link was pressed */
430 static gboolean
431 welcome_filename_link_press_cb(GtkWidget *widget _U_, GdkEventButton *event _U_, gpointer data)
432 {
433     menu_open_filename(data);
434
435     return FALSE;
436 }
437
438 typedef struct _recent_item_status {
439     gchar     *filename;
440     GtkWidget *label;
441     GObject   *menu_item;
442     GString   *str;
443     gboolean   stat_done;
444     int        err;
445     guint      timer;
446 } recent_item_status;
447
448 /*
449  * Fetch the status of a file.
450  * This function might be called as a thread. We can't use any drawing
451  * routines here: http://developer.gnome.org/gdk/2.24/gdk-Threads.html
452  */
453 static void *get_recent_item_status(void *data)
454 {
455     recent_item_status *ri_stat = (recent_item_status *) data;
456     ws_statb64 stat_buf;
457     int err;
458
459     if (!ri_stat) {
460         return NULL;
461     }
462
463     /*
464      * Add file size. We use binary prefixes instead of IEC because that's what
465      * most OSes use.
466      */
467     err = ws_stat64(ri_stat->filename, &stat_buf);
468     g_mutex_lock(recent_mtx);
469     ri_stat->err = err;
470     if(err == 0) {
471         if (stat_buf.st_size/1024/1024/1024 > 10) {
472             g_string_append_printf(ri_stat->str, " (%" G_GINT64_MODIFIER "d GB)", (gint64) (stat_buf.st_size/1024/1024/1024));
473         } else if (stat_buf.st_size/1024/1024 > 10) {
474             g_string_append_printf(ri_stat->str, " (%" G_GINT64_MODIFIER "d MB)", (gint64) (stat_buf.st_size/1024/1024));
475         } else if (stat_buf.st_size/1024 > 10) {
476             g_string_append_printf(ri_stat->str, " (%" G_GINT64_MODIFIER "d KB)", (gint64) (stat_buf.st_size/1024));
477         } else {
478             g_string_append_printf(ri_stat->str, " (%" G_GINT64_MODIFIER "d Bytes)", (gint64) (stat_buf.st_size));
479         }
480         /* pango format string */
481         g_string_prepend(ri_stat->str, "<span foreground='blue'>");
482         g_string_append(ri_stat->str, "</span>");
483     } else {
484         g_string_append(ri_stat->str, " [not found]");
485     }
486
487     if (!ri_stat->label) { /* The widget went away while we were busy. */
488         g_free(ri_stat->filename);
489         g_string_free(ri_stat->str, TRUE);
490         g_free(ri_stat);
491     } else {
492         ri_stat->stat_done = TRUE;
493     }
494     g_mutex_unlock(recent_mtx);
495
496     return NULL;
497 }
498
499 /* Timeout callback for recent items */
500 static gboolean
501 update_recent_items(gpointer data)
502 {
503     recent_item_status *ri_stat = (recent_item_status *) data;
504     gboolean again = TRUE;
505
506     if (!ri_stat) {
507         return FALSE;
508     }
509
510     g_mutex_lock(recent_mtx);
511
512     if (ri_stat->stat_done) {
513         again = FALSE;
514         gtk_label_set_markup(GTK_LABEL(ri_stat->label), ri_stat->str->str);
515         if (ri_stat->err == 0) {
516             gtk_widget_set_sensitive(ri_stat->label, TRUE);
517 #ifdef MAIN_MENU_USE_UIMANAGER
518             gtk_action_set_sensitive(GtkAction *) ri_stat->menu_item, TRUE);
519 #else
520             gtk_widget_set_sensitive(GTK_WIDGET(ri_stat->menu_item), TRUE);
521 #endif
522         }
523         ri_stat->timer = 0;
524     }
525     /* Else append some sort of Unicode or ASCII animation to the label? */
526     g_mutex_unlock(recent_mtx);
527
528     return again;
529 }
530
531 static void welcome_filename_destroy_cb(GtkWidget *w _U_, gpointer data) {
532     recent_item_status *ri_stat = (recent_item_status *) data;
533
534     if (!ri_stat) {
535         return;
536     }
537
538     g_mutex_lock(recent_mtx);
539     if (ri_stat->timer) {
540         g_source_remove(ri_stat->timer);
541         ri_stat->timer = 0;
542     }
543
544     g_object_unref(ri_stat->menu_item);
545
546     if (ri_stat->stat_done) {
547         g_free(ri_stat->filename);
548         g_string_free(ri_stat->str, TRUE);
549         g_free(ri_stat);
550     } else {
551         ri_stat->label = NULL;
552     }
553     g_mutex_unlock(recent_mtx);
554 }
555
556 /* create a "file link widget" */
557 static GtkWidget *
558 welcome_filename_link_new(const gchar *filename, GtkWidget **label, GObject *menu_item)
559 {
560     GtkWidget   *w;
561     GtkWidget   *eb;
562     GString     *str;
563     gchar       *str_escaped;
564     glong        uni_len;
565     gsize        uni_start, uni_end;
566     const glong  max = 60;
567     recent_item_status *ri_stat;
568
569     /* filename */
570     str = g_string_new(filename);
571     uni_len = g_utf8_strlen(str->str, str->len);
572
573     /* cut max filename length */
574     if (uni_len > max) {
575         uni_start = g_utf8_offset_to_pointer(str->str, 20) - str->str;
576         uni_end = g_utf8_offset_to_pointer(str->str, uni_len - max) - str->str;
577         g_string_erase(str, uni_start, uni_end);
578         g_string_insert(str, uni_start, " " UTF8_HORIZONTAL_ELLIPSIS " ");
579     }
580
581     /* escape the possibly shortened filename before adding pango language */
582     str_escaped=g_markup_escape_text(str->str, -1);
583     g_string_free(str, TRUE);
584
585     /* label */
586     w = gtk_label_new(str_escaped);
587     *label = w;
588     gtk_misc_set_padding(GTK_MISC(w), 5, 2);
589     gtk_misc_set_alignment (GTK_MISC(w), 0.0f, 0.0f);
590     gtk_widget_set_sensitive(w, FALSE);
591
592     ri_stat = g_malloc(sizeof(recent_item_status));
593     ri_stat->filename = g_strdup(filename);
594     ri_stat->label = w;
595     ri_stat->menu_item = menu_item;
596     ri_stat->str = g_string_new(str_escaped);
597     ri_stat->stat_done = FALSE;
598     ri_stat->timer = 0;
599     g_object_ref(G_OBJECT(menu_item));
600     g_signal_connect(w, "destroy", G_CALLBACK(welcome_filename_destroy_cb), ri_stat);
601     g_free(str_escaped);
602
603 #ifdef USE_THREADS
604     g_thread_create(get_recent_item_status, ri_stat, FALSE, NULL);
605     ri_stat->timer = g_timeout_add(200, update_recent_items, ri_stat);
606 #else
607     get_recent_item_status(ri_stat);
608     update_recent_items(ri_stat);
609 #endif
610
611     /* event box */
612     eb = gtk_event_box_new();
613     gtk_widget_modify_bg(eb, GTK_STATE_NORMAL, &topic_item_idle_bg);
614     gtk_container_add(GTK_CONTAINER(eb), w);
615     gtk_widget_set_tooltip_text(eb, filename);
616
617     g_signal_connect(eb, "enter-notify-event", G_CALLBACK(welcome_item_enter_cb), w);
618     g_signal_connect(eb, "leave-notify-event", G_CALLBACK(welcome_item_leave_cb), w);
619     g_signal_connect(eb, "button-press-event", G_CALLBACK(welcome_filename_link_press_cb), (gchar *) filename);
620
621     return eb;
622 }
623
624
625 /* reset the list of recent files */
626 void
627 main_welcome_reset_recent_capture_files(void)
628 {
629     GtkWidget *child_box;
630     GList* child_list;
631     GList* child_list_item;
632
633
634     if(welcome_file_panel_vb) {
635         child_box = scroll_box_dynamic_reset(welcome_file_panel_vb);
636         child_list = gtk_container_get_children(GTK_CONTAINER(child_box));
637         child_list_item = child_list;
638
639         while(child_list_item) {
640             gtk_container_remove(GTK_CONTAINER(child_box), child_list_item->data);
641             child_list_item = g_list_next(child_list_item);
642         }
643
644         g_list_free(child_list);
645     }
646 }
647
648
649 /* add a new file to the list of recent files */
650 void
651 main_welcome_add_recent_capture_file(const char *widget_cf_name, GObject *menu_item)
652 {
653     GtkWidget *w;
654     GtkWidget *child_box;
655     GtkWidget *label;
656
657
658     w = welcome_filename_link_new(widget_cf_name, &label, menu_item);
659     child_box = scroll_box_dynamic_add(welcome_file_panel_vb);
660     gtk_box_pack_start(GTK_BOX(child_box), w, FALSE, FALSE, 0);
661     gtk_widget_show_all(w);
662     gtk_widget_show_all(child_box);
663 }
664
665 #ifdef HAVE_LIBPCAP
666 static gboolean select_current_ifaces(GtkTreeModel  *model,
667                                   GtkTreePath   *path _U_,
668                                   GtkTreeIter   *iter,
669                                   gpointer       userdata)
670 {
671     guint i;
672     gchar *if_name;
673
674     GtkTreeSelection *selection = (GtkTreeSelection *)userdata;
675     gtk_tree_model_get (model, iter, 2, &if_name, -1);
676     if (global_capture_opts.ifaces->len > 0) {
677         for (i = 0; i < global_capture_opts.ifaces->len; i++) {
678             if (strcmp(g_array_index(global_capture_opts.ifaces, interface_options, i).name, if_name) == 0) {
679                 gtk_tree_selection_select_iter(selection, iter);
680                 break;
681             }
682         }
683     }
684     return FALSE;
685 }
686 #endif
687
688 /* list the interfaces */
689 void
690 welcome_if_tree_load(void)
691 {
692 #ifdef HAVE_LIBPCAP
693     if_info_t     *if_info;
694     GList         *if_list;
695     int err;
696     gchar         *err_str = NULL;
697     GList         *curr;
698     gchar         *user_descr;
699     GtkListStore  *store = NULL;
700     GtkTreeIter   iter;
701     GtkWidget     *icon, *view;
702     GtkTreeSelection *entry;
703
704     /* LOAD THE INTERFACES */
705     if_list = capture_interface_list(&err, &err_str);
706     if_list = g_list_sort (if_list, if_list_comparator_alph);
707     if (if_list == NULL && err == CANT_GET_INTERFACE_LIST) {
708         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_str);
709         g_free(err_str);
710         return;
711     } else if (err_str) {
712         g_free(err_str);
713     }
714     if (g_list_length(if_list) > 0) {
715         view = g_object_get_data(G_OBJECT(welcome_hb), TREE_VIEW_INTERFACES);
716         entry = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
717         gtk_tree_selection_unselect_all(entry);
718         store = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING);
719         /* List the interfaces */
720         for (curr = g_list_first(if_list); curr; curr = g_list_next(curr)) {
721             if_info = curr->data;
722             /* Continue if capture device is hidden */
723             if (prefs_is_capture_device_hidden(if_info->name)) {
724                 continue;
725             }
726             gtk_list_store_append (store, &iter);
727
728 #ifdef HAVE_AIRPCAP
729             if (get_airpcap_if_from_name(airpcap_if_list,if_info->name) != NULL)
730                 icon = xpm_to_widget(capture_airpcap_16_xpm);
731             else
732                 icon = capture_get_if_icon(if_info);
733 #else
734             icon = capture_get_if_icon(if_info);
735 #endif
736             user_descr = capture_dev_user_descr_find(if_info->name);
737             if (user_descr) {
738 #ifndef _WIN32
739                 gchar *comment = user_descr;
740                 user_descr = g_strdup_printf("%s (%s)", comment, if_info->name);
741                 g_free (comment);
742 #endif
743                 gtk_list_store_set(store, &iter, 0, gtk_image_get_pixbuf(GTK_IMAGE(icon)), 1, user_descr, 2, if_info->name, -1);
744                 g_free (user_descr);
745             } else if (if_info->description) {
746                 gtk_list_store_set (store, &iter, 0, gtk_image_get_pixbuf(GTK_IMAGE(icon)), 1, if_info->description, 2, if_info->name, -1);
747             } else {
748                 gtk_list_store_set (store, &iter, 0, gtk_image_get_pixbuf(GTK_IMAGE(icon)), 1, if_info->name, 2, if_info->name, -1);
749             }
750         }
751         gtk_tree_view_set_model(GTK_TREE_VIEW(if_view), GTK_TREE_MODEL (store));
752         if (global_capture_opts.ifaces->len > 0) {
753             gtk_tree_model_foreach(GTK_TREE_MODEL(store), select_current_ifaces, (gpointer) entry);
754             gtk_widget_grab_focus(view);
755         }
756     }
757     free_interface_list(if_list);
758 #endif  /* HAVE_LIBPCAP */
759 }
760
761
762 /* reload the list of interfaces */
763 void
764 welcome_if_panel_reload(void)
765 {
766 #ifdef HAVE_LIBPCAP
767     GtkWidget *child_box;
768     GList* child_list;
769     GList* child_list_item;
770
771
772     if(welcome_if_panel_vb) {
773         child_box = scroll_box_dynamic_reset(welcome_if_panel_vb);
774         child_list = gtk_container_get_children(GTK_CONTAINER(child_box));
775         child_list_item = child_list;
776
777         while(child_list_item) {
778             gtk_container_remove(GTK_CONTAINER(child_box), child_list_item->data);
779             child_list_item = g_list_next(child_list_item);
780         }
781
782         g_list_free(child_list);
783         welcome_if_tree_load();
784         gtk_widget_show_all(welcome_if_panel_vb);
785     }
786 #endif  /* HAVE_LIBPCAP */
787 }
788
789 #ifdef HAVE_LIBPCAP
790 static void make_selections_array(GtkTreeModel  *model,
791                                   GtkTreePath   *path _U_,
792                                   GtkTreeIter   *iter,
793                                   gpointer       userdata _U_)
794 {
795   gchar            *if_name;
796   interface_options interface_opts;
797   cap_settings_t    cap_settings;
798   GList            *if_list;
799   GList            *curr;
800   int               err;
801   if_info_t        *if_info;
802
803   gtk_tree_model_get (model, iter, 2, &if_name, -1);
804
805   if_list = capture_interface_list(&err, NULL);
806   if_list = g_list_sort (if_list, if_list_comparator_alph);
807   if (g_list_length(if_list) > 0) {
808       for (curr = g_list_first(if_list); curr; curr = g_list_next(curr)) {
809           if_info = curr->data;
810           /* Continue if capture device is hidden */
811           if (prefs_is_capture_device_hidden(if_info->name)) {
812               continue;
813           }
814           if (strcmp(if_info->name, if_name) == 0) {
815               interface_opts.name = g_strdup(if_name);
816               interface_opts.descr = get_interface_descriptive_name(interface_opts.name);
817               break;
818           }
819       }
820       free_interface_list(if_list);
821   } else {
822       free_interface_list(if_list);
823       return;
824   }
825
826   interface_opts.linktype = capture_dev_user_linktype_find(interface_opts.name);
827   interface_opts.cfilter = g_strdup(global_capture_opts.default_options.cfilter);
828   interface_opts.has_snaplen = global_capture_opts.default_options.has_snaplen;
829   interface_opts.snaplen = global_capture_opts.default_options.snaplen;
830   cap_settings = capture_get_cap_settings (interface_opts.name);;
831   interface_opts.promisc_mode = global_capture_opts.default_options.promisc_mode;
832 #if defined(_WIN32) || defined(HAVE_PCAP_CREATE)
833   interface_opts.buffer_size =  global_capture_opts.default_options.buffer_size;
834 #endif
835   interface_opts.monitor_mode = cap_settings.monitor_mode;
836 #ifdef HAVE_PCAP_REMOTE
837   interface_opts.src_type = global_capture_opts.default_options.src_type;
838   interface_opts.remote_host = g_strdup(global_capture_opts.default_options.remote_host);
839   interface_opts.remote_port = g_strdup(global_capture_opts.default_options.remote_port);
840   interface_opts.auth_type = global_capture_opts.default_options.auth_type;
841   interface_opts.auth_username = g_strdup(global_capture_opts.default_options.auth_username);
842   interface_opts.auth_password = g_strdup(global_capture_opts.default_options.auth_password);
843   interface_opts.datatx_udp = global_capture_opts.default_options.datatx_udp;
844   interface_opts.nocap_rpcap = global_capture_opts.default_options.nocap_rpcap;
845   interface_opts.nocap_local = global_capture_opts.default_options.nocap_local;
846 #endif
847 #ifdef HAVE_PCAP_SETSAMPLING
848   interface_opts.sampling_method = global_capture_opts.default_options.sampling_method;
849   interface_opts.sampling_param  = global_capture_opts.default_options.sampling_param;
850 #endif
851   g_array_append_val(global_capture_opts.ifaces, interface_opts);
852 }
853
854 static void capture_if_start(GtkWidget *w _U_, gpointer data _U_)
855 {
856   GtkTreeSelection *entry;
857   GtkWidget*    view;
858   gint len;
859   interface_options  interface_opts;
860
861   view = g_object_get_data(G_OBJECT(welcome_hb), TREE_VIEW_INTERFACES);
862   entry = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
863   len = gtk_tree_selection_count_selected_rows(entry);
864   if (!entry || len==0) {
865     simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
866       "You didn't specify an interface on which to capture packets.");
867     return;
868   }
869   while (global_capture_opts.ifaces->len > 0) {
870     interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, 0);
871     global_capture_opts.ifaces = g_array_remove_index(global_capture_opts.ifaces, 0);
872     g_free(interface_opts.name);
873     g_free(interface_opts.descr);
874     g_free(interface_opts.cfilter);
875 #ifdef HAVE_PCAP_REMOTE
876     g_free(interface_opts.remote_host);
877     g_free(interface_opts.remote_port);
878     g_free(interface_opts.auth_username);
879     g_free(interface_opts.auth_password);
880 #endif
881   }
882   gtk_tree_selection_selected_foreach(entry, make_selections_array, NULL);
883
884   /* XXX - remove this? */
885   if (global_capture_opts.save_file) {
886       g_free(global_capture_opts.save_file);
887       global_capture_opts.save_file = NULL;
888   }
889 #ifdef HAVE_AIRPCAP
890   interface_opts = g_array_index(global_capture_opts.ifaces, interface_options, 0);
891   airpcap_if_active = get_airpcap_if_from_name(airpcap_if_list, interface_opts.name);
892   airpcap_if_selected = airpcap_if_active;
893   airpcap_set_toolbar_start_capture(airpcap_if_active);
894 #endif
895   capture_start_cb(NULL, NULL);
896 }
897
898 void capture_if_cb_prep(GtkWidget *w _U_, gpointer d _U_)
899 {
900   GtkTreeSelection *entry;
901   GtkWidget* view;
902
903   view = g_object_get_data(G_OBJECT(welcome_hb), TREE_VIEW_INTERFACES);
904   entry = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
905   if (entry) {
906    /* global_capture_opts.number_of_ifaces = gtk_tree_selection_count_selected_rows(entry);*/
907     gtk_tree_selection_selected_foreach(entry, make_selections_array, NULL);
908   }
909   capture_if_cb(NULL, NULL);
910 }
911
912 void capture_opts_cb_prep(GtkWidget *w _U_, gpointer d _U_)
913 {
914   GtkTreeSelection *entry;
915   GtkWidget* view;
916
917   view = g_object_get_data(G_OBJECT(welcome_hb), TREE_VIEW_INTERFACES);
918   entry = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
919   if (entry) {
920     gtk_tree_selection_selected_foreach(entry, make_selections_array, NULL);
921   }
922   capture_prep_cb(NULL, NULL);
923 }
924 #endif
925
926 /* create the welcome page */
927 GtkWidget *
928 welcome_new(void)
929 {
930     GtkWidget *welcome_scrollw;
931     GtkWidget *welcome_eb;
932     GtkWidget *welcome_vb;
933     GtkWidget *column_vb;
934     GtkWidget *item_hb;
935     GtkWidget *w;
936     GtkWidget *header;
937     GtkWidget *topic_vb;
938     GtkWidget *topic_to_fill;
939     GtkWidget *file_child_box;
940     gchar *label_text;
941 #ifdef _WIN32
942     LONG reg_ret;
943     DWORD chimney_enabled = 0;
944     DWORD ce_size = sizeof(chimney_enabled);
945 #endif
946 #ifdef HAVE_LIBPCAP
947     GtkWidget *swindow;
948     GtkTreeSelection *selection;
949     GtkCellRenderer *renderer;
950     GtkTreeViewColumn *column;
951     GList     *if_list;
952     int err;
953     gchar *err_str = NULL;
954 #endif
955
956     /* prepare colors */
957
958     /* "page" background */
959     get_color(&welcome_bg);
960
961     /* header bar background color */
962     get_color(&header_bar_bg);
963
964     /* topic header background color */
965     get_color(&topic_header_bg);
966
967     /* topic content background color */
968     get_color(&topic_content_bg);
969
970     topic_item_idle_bg = topic_content_bg;
971
972     /* topic item entered color */
973     get_color(&topic_item_entered_bg);
974
975     welcome_scrollw = scrolled_window_new(NULL, NULL);
976
977     welcome_vb = gtk_vbox_new(FALSE, 0);
978
979     welcome_eb = gtk_event_box_new();
980     gtk_container_add(GTK_CONTAINER(welcome_eb), welcome_vb);
981     gtk_widget_modify_bg(welcome_eb, GTK_STATE_NORMAL, &welcome_bg);
982
983     /* header */
984     header = welcome_header_new();
985     gtk_box_pack_start(GTK_BOX(welcome_vb), header, FALSE, FALSE, 0);
986
987     /* content */
988     welcome_hb = gtk_hbox_new(FALSE, 10);
989     gtk_container_set_border_width(GTK_CONTAINER(welcome_hb), 10);
990     gtk_box_pack_start(GTK_BOX(welcome_vb), welcome_hb, TRUE, TRUE, 0);
991
992
993     /* column capture */
994     column_vb = gtk_vbox_new(FALSE, 10);
995     gtk_widget_modify_bg(column_vb, GTK_STATE_NORMAL, &welcome_bg);
996     gtk_box_pack_start(GTK_BOX(welcome_hb), column_vb, TRUE, TRUE, 0);
997
998     /* capture topic */
999     topic_vb = welcome_topic_new("Capture", &topic_to_fill);
1000     gtk_box_pack_start(GTK_BOX(column_vb), topic_vb, TRUE, TRUE, 0);
1001
1002 #ifdef HAVE_LIBPCAP
1003     if_list = capture_interface_list(&err, &err_str);
1004     if (g_list_length(if_list) > 0) {
1005         item_hb = welcome_button(WIRESHARK_STOCK_CAPTURE_INTERFACES,
1006             "Interface List",
1007             "Live list of the capture interfaces\n(counts incoming packets)",
1008             "Same as Capture/Interfaces menu or toolbar item",
1009             welcome_button_callback_helper, capture_if_cb_prep);
1010         gtk_box_pack_start(GTK_BOX(topic_to_fill), item_hb, FALSE, FALSE, 5);
1011
1012         swindow = gtk_scrolled_window_new (NULL, NULL);
1013         gtk_widget_set_size_request(swindow, FALSE, 100);
1014         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(swindow), GTK_SHADOW_IN);
1015         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
1016
1017         if_view = gtk_tree_view_new ();
1018         g_object_set(GTK_OBJECT(if_view), "headers-visible", FALSE, NULL);
1019         g_object_set_data(G_OBJECT(welcome_hb), TREE_VIEW_INTERFACES, if_view);
1020         renderer = gtk_cell_renderer_pixbuf_new();
1021         column = gtk_tree_view_column_new_with_attributes ("",
1022                                                GTK_CELL_RENDERER(renderer),
1023                                                "pixbuf", 0,
1024                                                NULL);
1025         gtk_tree_view_append_column(GTK_TREE_VIEW(if_view), column);
1026         renderer = gtk_cell_renderer_text_new();
1027         column = gtk_tree_view_column_new_with_attributes ("",
1028                                                GTK_CELL_RENDERER(renderer),
1029                                                "text", 1,
1030                                                NULL);
1031         gtk_tree_view_append_column(GTK_TREE_VIEW(if_view), column);
1032         gtk_tree_view_column_set_resizable(gtk_tree_view_get_column(GTK_TREE_VIEW(if_view), 0), TRUE);
1033         renderer = gtk_cell_renderer_text_new();
1034         column = gtk_tree_view_column_new_with_attributes ("",
1035                                                GTK_CELL_RENDERER(renderer),
1036                                                "text", 2,
1037                                                NULL);
1038         gtk_tree_view_append_column(GTK_TREE_VIEW(if_view), column);
1039         gtk_tree_view_column_set_visible(column, FALSE);
1040         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(if_view));
1041         gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
1042         item_hb = welcome_button(WIRESHARK_STOCK_CAPTURE_START,
1043             "Start",
1044             "Choose one or more interfaces to capture from, then <b>Start</b>",
1045             "Same as Capture/Interfaces with default options",
1046             (welcome_button_callback_t)capture_if_start, (gpointer)if_view);
1047         gtk_box_pack_start(GTK_BOX(topic_to_fill), item_hb, FALSE, FALSE, 5);
1048         welcome_if_tree_load();
1049         gtk_container_add (GTK_CONTAINER (swindow), if_view);
1050         gtk_container_add(GTK_CONTAINER(topic_to_fill), swindow);
1051
1052         item_hb = welcome_button(WIRESHARK_STOCK_CAPTURE_OPTIONS,
1053                 "Capture Options",
1054                 "Start a capture with detailed options",
1055                 "Same as Capture/Options menu or toolbar item",
1056                 welcome_button_callback_helper, capture_prep_cb);
1057         gtk_box_pack_start(GTK_BOX(topic_to_fill), item_hb, FALSE, FALSE, 5);
1058 #ifdef _WIN32
1059         /* Check for chimney offloading */
1060         reg_ret = RegQueryValueEx(HKEY_LOCAL_MACHINE,
1061                                   _T("SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\EnableTCPChimney"),
1062                                   NULL, NULL, (LPBYTE) &chimney_enabled, &ce_size);
1063         if (reg_ret == ERROR_SUCCESS && chimney_enabled) {
1064             item_hb = welcome_button(WIRESHARK_STOCK_WIKI,
1065                     "Offloading Detected",
1066                     "TCP Chimney offloading is enabled. You \nmight not capture much data.",
1067                     topic_online_url(ONLINEPAGE_CHIMNEY),
1068                     topic_menu_cb, GINT_TO_POINTER(ONLINEPAGE_CHIMNEY));
1069             gtk_box_pack_start(GTK_BOX(topic_to_fill), item_hb, FALSE, FALSE, 5);
1070         }
1071 #endif /* _WIN32 */
1072     } else {
1073         label_text =  g_strdup("No interface can be used for capturing in\n"
1074                                "this system with the current configuration.\n\n"
1075                                "See Capture Help below for details.");
1076         w = gtk_label_new(label_text);
1077         gtk_label_set_markup(GTK_LABEL(w), label_text);
1078         g_free (label_text);
1079         gtk_misc_set_alignment (GTK_MISC(w), 0.0f, 0.0f);
1080         gtk_box_pack_start(GTK_BOX(topic_to_fill), w, FALSE, FALSE, 5);
1081     }
1082
1083     free_interface_list(if_list);
1084
1085     /* capture help topic */
1086     topic_vb = welcome_topic_new("Capture Help", &topic_to_fill);
1087     gtk_box_pack_start(GTK_BOX(column_vb), topic_vb, TRUE, TRUE, 0);
1088
1089     item_hb = welcome_button(WIRESHARK_STOCK_WIKI,
1090         "How to Capture",
1091         "Step by step to a successful capture setup",
1092         topic_online_url(ONLINEPAGE_CAPTURE_SETUP),
1093         topic_menu_cb, GINT_TO_POINTER(ONLINEPAGE_CAPTURE_SETUP));
1094     gtk_box_pack_start(GTK_BOX(topic_to_fill), item_hb, FALSE, FALSE, 5);
1095
1096     item_hb = welcome_button(WIRESHARK_STOCK_WIKI,
1097         "Network Media",
1098         "Specific information for capturing on:\nEthernet, WLAN, ...",
1099         topic_online_url(ONLINEPAGE_NETWORK_MEDIA),
1100         topic_menu_cb, GINT_TO_POINTER(ONLINEPAGE_NETWORK_MEDIA));
1101     gtk_box_pack_start(GTK_BOX(topic_to_fill), item_hb, FALSE, FALSE, 5);
1102 #else
1103     label_text =  g_strdup("<span foreground=\"black\">Capturing is not compiled into this version of Wireshark!</span>");
1104     w = gtk_label_new(label_text);
1105     gtk_label_set_markup(GTK_LABEL(w), label_text);
1106     g_free (label_text);
1107     gtk_misc_set_alignment (GTK_MISC(w), 0.0f, 0.0f);
1108     gtk_box_pack_start(GTK_BOX(topic_to_fill), w, FALSE, FALSE, 5);
1109 #endif  /* HAVE_LIBPCAP */
1110
1111     /* fill bottom space */
1112     w = gtk_label_new("");
1113     gtk_box_pack_start(GTK_BOX(topic_to_fill), w, TRUE, TRUE, 0);
1114
1115
1116     /* column files */
1117     topic_vb = welcome_topic_new("Files", &topic_to_fill);
1118     gtk_box_pack_start(GTK_BOX(welcome_hb), topic_vb, TRUE, TRUE, 0);
1119
1120     item_hb = welcome_button(GTK_STOCK_OPEN,
1121         "Open",
1122         "Open a previously captured file",
1123         "Same as File/Open menu or toolbar item",
1124         welcome_button_callback_helper, file_open_cmd_cb);
1125     gtk_box_pack_start(GTK_BOX(topic_to_fill), item_hb, FALSE, FALSE, 5);
1126
1127     /* prepare list of recent files (will be filled in later) */
1128     label_text =  g_strdup("<span foreground=\"black\">Open Recent:</span>");
1129     w = gtk_label_new(label_text);
1130     gtk_label_set_markup(GTK_LABEL(w), label_text);
1131     g_free (label_text);
1132     gtk_misc_set_alignment (GTK_MISC(w), 0.0f, 0.0f);
1133     gtk_box_pack_start(GTK_BOX(topic_to_fill), w, FALSE, FALSE, 5);
1134
1135     file_child_box = gtk_vbox_new(FALSE, 1);
1136     /* 17 file items or 300 pixels height is about the size */
1137     /* that still fits on a screen of about 1000*700 */
1138     welcome_file_panel_vb = scroll_box_dynamic_new(GTK_WIDGET(file_child_box), 17, 300);
1139     gtk_box_pack_start(GTK_BOX(topic_to_fill), welcome_file_panel_vb, FALSE, FALSE, 0);
1140
1141     item_hb = welcome_button(WIRESHARK_STOCK_WIKI,
1142         "Sample Captures",
1143         "A rich assortment of example capture files on the wiki",
1144         topic_online_url(ONLINEPAGE_SAMPLE_CAPTURES),
1145         topic_menu_cb, GINT_TO_POINTER(ONLINEPAGE_SAMPLE_CAPTURES));
1146     gtk_box_pack_start(GTK_BOX(topic_to_fill), item_hb, FALSE, FALSE, 5);
1147
1148     /* fill bottom space */
1149     w = gtk_label_new("");
1150     gtk_box_pack_start(GTK_BOX(topic_to_fill), w, TRUE, TRUE, 0);
1151
1152
1153     /* column online */
1154     column_vb = gtk_vbox_new(FALSE, 10);
1155     gtk_box_pack_start(GTK_BOX(welcome_hb), column_vb, TRUE, TRUE, 0);
1156
1157     /* topic online */
1158     topic_vb = welcome_topic_new("Online", &topic_to_fill);
1159     gtk_box_pack_start(GTK_BOX(column_vb), topic_vb, TRUE, TRUE, 0);
1160
1161     item_hb = welcome_button(GTK_STOCK_HOME,
1162         "Website",
1163         "Visit the project's website",
1164         topic_online_url(ONLINEPAGE_HOME),
1165         topic_menu_cb, GINT_TO_POINTER(ONLINEPAGE_HOME));
1166     gtk_box_pack_start(GTK_BOX(topic_to_fill), item_hb, FALSE, FALSE, 5);
1167
1168 #ifdef HHC_DIR
1169     item_hb = welcome_button(GTK_STOCK_HELP,
1170         "User's Guide",
1171         "The User's Guide "
1172         "(local version, if installed)",
1173         "Locally installed (if installed) otherwise online version",
1174         topic_menu_cb, GINT_TO_POINTER(HELP_CONTENT));
1175 #else
1176     item_hb = welcome_button(GTK_STOCK_HELP,
1177         "User's Guide",
1178         "The User's Guide "
1179         "(online version)",
1180         topic_online_url(ONLINEPAGE_USERGUIDE),
1181         topic_menu_cb, GINT_TO_POINTER(ONLINEPAGE_USERGUIDE));
1182 #endif
1183     gtk_box_pack_start(GTK_BOX(topic_to_fill), item_hb, FALSE, FALSE, 5);
1184
1185     item_hb = welcome_button(WIRESHARK_STOCK_WIKI,
1186         "Security",
1187         "Work with Wireshark as securely as possible",
1188         topic_online_url(ONLINEPAGE_SECURITY),
1189         topic_menu_cb, GINT_TO_POINTER(ONLINEPAGE_SECURITY));
1190     gtk_box_pack_start(GTK_BOX(topic_to_fill), item_hb, FALSE, FALSE, 5);
1191
1192 #if 0
1193     /* XXX - add this, once the Windows update functionality is implemented */
1194     /* topic updates */
1195     topic_vb = welcome_topic_new("Updates", &topic_to_fill);
1196     gtk_box_pack_start(GTK_BOX(column_vb), topic_vb, TRUE, TRUE, 0);
1197
1198     label_text =  g_strdup("<span foreground=\"black\">No updates available!</span>");
1199     w = gtk_label_new(label_text);
1200     gtk_label_set_markup(GTK_LABEL(w), label_text);
1201     g_free (label_text);
1202     gtk_box_pack_start(GTK_BOX(topic_to_fill), w, TRUE, TRUE, 0);
1203 #endif
1204
1205
1206     /* the end */
1207     gtk_widget_show_all(welcome_eb);
1208
1209     gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(welcome_scrollw),
1210                                           welcome_eb);
1211     gtk_widget_show_all(welcome_scrollw);
1212
1213     recent_mtx = g_mutex_new();
1214
1215     return welcome_scrollw;
1216 }