Annotation of embedaddon/mtr/ui/gtk.c, revision 1.1.1.2
1.1 misho 1: /*
2: mtr -- a network diagnostic tool
3: Copyright (C) 1997,1998 Matt Kimball
4: Changes/additions Copyright (C) 1998 R.E.Wolff@BitWizard.nl
5:
6: This program is free software; you can redistribute it and/or modify
7: it under the terms of the GNU General Public License version 2 as
8: published by the Free Software Foundation.
9:
10: This program is distributed in the hope that it will be useful,
11: but WITHOUT ANY WARRANTY; without even the implied warranty of
12: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13: GNU General Public License for more details.
14:
1.1.1.2 ! misho 15: You should have received a copy of the GNU General Public License along
! 16: with this program; if not, write to the Free Software Foundation, Inc.,
! 17: 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
1.1 misho 18: */
19:
20: #include "config.h"
21:
22: #include <stdio.h>
23: #include <stdlib.h>
24: #include <unistd.h>
25: #include <sys/time.h>
26: #include <sys/types.h>
27:
28: #ifdef HAVE_GTK
29: #include <string.h>
30: #include <sys/types.h>
31: #include <gtk/gtk.h>
32:
33: #include "mtr.h"
34: #include "net.h"
35: #include "dns.h"
36: #include "asn.h"
37: #include "mtr-gtk.h"
38: #include "utils.h"
39:
40: #include "img/mtr_icon.xpm"
41: #endif
42:
43: static gint gtk_ping(
44: gpointer data);
45: static gint Copy_activate(
46: GtkWidget * widget,
47: gpointer data);
48: static gint NewDestination_activate(
49: GtkWidget * widget,
50: gpointer data);
51: static gboolean ReportTreeView_clicked(
52: GtkWidget * Tree,
53: GdkEventButton * event,
54: gpointer data);
55: static gchar *getSelectedHost(
56: GtkTreePath * path);
57:
58: static int ping_timeout_timer;
59: static GtkWidget *Pause_Button;
60: static GtkWidget *Entry;
61: static GtkWidget *main_window;
62:
63: static void gtk_add_ping_timeout(
64: struct mtr_ctl *ctl)
65: {
66: int dt;
67:
68: if (gtk_toggle_button_get_active((GtkToggleButton *) Pause_Button)) {
69: return;
70: }
71: dt = calc_deltatime(ctl->WaitTime);
72: gtk_redraw(ctl);
73: ping_timeout_timer = g_timeout_add(dt / 1000, gtk_ping, ctl);
74: }
75:
76:
77: static void gtk_do_init(
78: int *argc,
79: char ***argv)
80: {
81: static int done = 0;
82:
83: if (!done) {
84: gtk_init(argc, argv);
85:
86: done = 1;
87: }
88: }
89:
90:
91: int gtk_detect(
92: ATTRIBUTE_UNUSED int *argc,
93: ATTRIBUTE_UNUSED char ***argv)
94: {
95: if (getenv("DISPLAY") != NULL) {
96: /* If we do this here, gtk_init exits on an error. This happens
97: BEFORE the user has had a chance to tell us not to use the
98: display... */
99: return TRUE;
100: } else {
101: return FALSE;
102: }
103: }
104:
105:
106: static gint Window_destroy(
107: ATTRIBUTE_UNUSED GtkWidget * Window,
108: ATTRIBUTE_UNUSED gpointer data)
109: {
110: gtk_main_quit();
111:
112: return FALSE;
113: }
114:
115:
116: static gint Restart_clicked(
117: ATTRIBUTE_UNUSED GtkWidget * Button,
118: gpointer data)
119: {
120: struct mtr_ctl *ctl = (struct mtr_ctl *) data;
121:
122: net_reset(ctl);
123: gtk_redraw(ctl);
124:
125: return FALSE;
126: }
127:
128:
129: static gint Pause_clicked(
130: ATTRIBUTE_UNUSED GtkWidget * Button,
131: gpointer data)
132: {
133: struct mtr_ctl *ctl = (struct mtr_ctl *) data;
134:
135: static int paused = 0;
136:
137: if (paused) {
138: gtk_add_ping_timeout(ctl);
139: } else {
140: g_source_remove(ping_timeout_timer);
141: }
142: paused = !paused;
143: gtk_redraw(ctl);
144:
145: return FALSE;
146: }
147:
148: static gint About_clicked(
149: ATTRIBUTE_UNUSED GtkWidget * Button,
150: ATTRIBUTE_UNUSED gpointer data)
151: {
152: static const gchar *authors[] = {
153: "Matt Kimball <matt.kimball@gmail.com>",
154: "Roger Wolff <R.E.Wolff@BitWizard.nl>",
155: "Bohdan Vlasyuk <bohdan@cec.vstu.vinnica.ua>",
156: "Evgeniy Tretyak <evtr@ukr.net>",
157: "John Thacker <thacker@math.cornell.edu>",
158: "Juha Takala",
159: "David Sward <sward@clark.net>",
160: "David Stone <stone@AsIf.com>",
161: "Andrew Stesin",
162: "Greg Stark <gsstark@mit.edu>",
163: "Robert Sparks <rjsparks@nostrum.com>",
164: "Mike Simons <msimons@moria.simons-clan.com>",
165: "Aaron Scarisbrick,",
166: "Craig Milo Rogers <Rogers@ISI.EDU>",
167: "Antonio Querubin <tony@lavanauts.org>",
168: "Russell Nelson <rn-mtr@crynwr.com>",
169: "Davin Milun <milun@acm.org>",
170: "Josh Martin <jmartin@columbiaservices.net>",
171: "Alexander V. Lukyanov <lav@yars.free.net>",
172: "Charles Levert <charles@comm.polymtl.ca> ",
173: "Bertrand Leconte <B.Leconte@mail.dotcom.fr>",
174: "Anand Kumria",
175: "Olav Kvittem <Olav.Kvittem@uninett.no>",
176: "Adam Kramer <l3zqc@qcunix1.acc.qc.edu> ",
177: "Philip Kizer <pckizer@nostrum.com>",
178: "Simon Kirby",
179: "Sami Kerola <kerolasa@iki.fi>",
180: "Christophe Kalt",
181: "Steve Kann <stevek@spheara.horizonlive.com>",
182: "Brett Johnson <brett@jdacareers.com>",
183: "Roland Illig <roland.illig@gmx.de>",
184: "Damian Gryski <dgryski@uwaterloo.ca>",
185: "Rob Foehl <rwf@loonybin.net>",
186: "Mircea Damian",
187: "Cougar <cougar@random.ee>",
188: "Travis Cross <tc@traviscross.com>",
189: "Brian Casey",
190: "Andrew Brown <atatat@atatdot.net>",
191: "Bill Bogstad <bogstad@pobox.com> ",
192: "Marc Bejarano <marc.bejarano@openwave.com>",
193: "Moritz Barsnick <barsnick@gmx.net>",
194: "Thomas Klausner <wiz@NetBSD.org>",
195: NULL
196: };
197:
198: gtk_show_about_dialog(GTK_WINDOW(main_window)
199: , "version", PACKAGE_VERSION, "copyright",
200: "Copyright \xc2\xa9 1997,1998 Matt Kimball",
1.1.1.2 ! misho 201: "website", "https://www.bitwizard.nl/mtr/",
1.1 misho 202: "authors", authors, "comments",
203: "The 'traceroute' and 'ping' programs in a single network diagnostic tool.",
204: "license",
205: "This program is free software; you can redistribute it and/or modify\n"
206: "it under the terms of the GNU General Public License version 2 as\n"
207: "published by the Free Software Foundation.\n"
208: "\n"
209: "This program is distributed in the hope that it will be useful,\n"
210: "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
211: "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n"
212: "GNU General Public License for more details.",
213: NULL);
214: return TRUE;
215: }
216:
217: /*
218: * There is a small problem with the following code:
219: * The timeout is canceled and removed in order to ensure that
220: * it takes effect (consider what happens if you set the timeout to 999,
221: * then try to undo the change); is a better approach possible?
222: *
223: * What's the problem with this? (-> "I don't think so)
224: */
225:
226: static gint WaitTime_changed(
227: ATTRIBUTE_UNUSED GtkAdjustment * Adj,
228: GtkWidget * data)
229: {
230: struct mtr_ctl *ctl = (struct mtr_ctl *) data;
231: GtkWidget *Button = (GtkWidget *) ctl->gtk_data;
232:
233: ctl->WaitTime = gtk_spin_button_get_value(GTK_SPIN_BUTTON(Button));
234: g_source_remove(ping_timeout_timer);
235: gtk_add_ping_timeout(ctl);
236: gtk_redraw(ctl);
237:
238: return FALSE;
239: }
240:
241:
242: static gint Host_activate(
243: GtkWidget * entry,
244: gpointer data)
245: {
246: struct mtr_ctl *ctl = (struct mtr_ctl *) data;
247: struct hostent *addr;
248:
249: addr = dns_forward(gtk_entry_get_text(GTK_ENTRY(entry)));
250: if (addr) {
251: net_reopen(ctl, addr);
252: /* If we are "Paused" at this point it is usually because someone
253: entered a non-existing host. Therefore do the go-ahead... */
254: gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(Pause_Button), 0);
255: } else {
256: int pos = strlen(gtk_entry_get_text(GTK_ENTRY(entry)));
257: gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(Pause_Button), 1);
258: gtk_editable_insert_text(GTK_EDITABLE(entry), ": not found", -1,
259: &pos);
260: }
261:
262: return FALSE;
263: }
264:
265:
266:
267: static void Toolbar_fill(
268: struct mtr_ctl *ctl,
269: GtkWidget * Toolbar)
270: {
271: GtkWidget *Button;
272: GtkWidget *Label;
273: GtkAdjustment *Adjustment;
274:
1.1.1.2 ! misho 275: Button = gtk_button_new_with_label("Quit");
1.1 misho 276: gtk_box_pack_end(GTK_BOX(Toolbar), Button, FALSE, FALSE, 0);
1.1.1.2 ! misho 277: g_signal_connect(G_OBJECT(Button), "clicked",
! 278: G_CALLBACK(Window_destroy), NULL);
1.1 misho 279:
1.1.1.2 ! misho 280: Button = gtk_button_new_with_label("About");
1.1 misho 281: gtk_box_pack_end(GTK_BOX(Toolbar), Button, FALSE, FALSE, 0);
1.1.1.2 ! misho 282: g_signal_connect(G_OBJECT(Button), "clicked",
! 283: G_CALLBACK(About_clicked), NULL);
1.1 misho 284:
285: Button = gtk_button_new_with_mnemonic("_Restart");
286: gtk_box_pack_end(GTK_BOX(Toolbar), Button, FALSE, FALSE, 0);
1.1.1.2 ! misho 287: g_signal_connect(G_OBJECT(Button), "clicked",
! 288: G_CALLBACK(Restart_clicked), ctl);
1.1 misho 289:
290: Pause_Button = gtk_toggle_button_new_with_mnemonic("_Pause");
291: gtk_box_pack_end(GTK_BOX(Toolbar), Pause_Button, FALSE, FALSE, 0);
1.1.1.2 ! misho 292: g_signal_connect(G_OBJECT(Pause_Button), "clicked",
! 293: G_CALLBACK(Pause_clicked), ctl);
1.1 misho 294:
295: /* allow root only to set zero delay */
296: Adjustment = (GtkAdjustment *) gtk_adjustment_new(ctl->WaitTime,
1.1.1.2 ! misho 297: running_as_root() ? 0.01 : 1.00,
1.1 misho 298: 999.99, 1.0, 10.0,
299: 0.0);
300: Button = gtk_spin_button_new(Adjustment, 0.5, 2);
301: gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(Button), TRUE);
302: gtk_box_pack_end(GTK_BOX(Toolbar), Button, FALSE, FALSE, 0);
303: ctl->gtk_data = Button;
1.1.1.2 ! misho 304: g_signal_connect(G_OBJECT(Adjustment), "value_changed",
! 305: G_CALLBACK(WaitTime_changed), ctl);
1.1 misho 306:
307: Label = gtk_label_new_with_mnemonic("_Hostname:");
308: gtk_box_pack_start(GTK_BOX(Toolbar), Label, FALSE, FALSE, 0);
309:
310: Entry = gtk_entry_new();
311: gtk_entry_set_text(GTK_ENTRY(Entry), ctl->Hostname);
1.1.1.2 ! misho 312: g_signal_connect(G_OBJECT(Entry), "activate",
! 313: G_CALLBACK(Host_activate), ctl);
1.1 misho 314: gtk_box_pack_start(GTK_BOX(Toolbar), Entry, TRUE, TRUE, 0);
315:
316: gtk_label_set_mnemonic_widget(GTK_LABEL(Label), Entry);
317: }
318:
319: static GtkWidget *ReportTreeView;
320: static GtkListStore *ReportStore;
321:
322: enum {
323: #ifdef HAVE_IPINFO
324: COL_ASN,
325: #endif
326: COL_HOSTNAME,
327: COL_LOSS,
328: COL_RCV,
329: COL_SNT,
330: COL_LAST,
331: COL_BEST,
332: COL_AVG,
333: COL_WORST,
334: COL_STDEV,
335: COL_COLOR,
336: N_COLS
337: };
338:
339: /* Trick to cast a pointer to integer. We are mis-using a pointer as a
340: single integer. On 64-bit architectures, the pointer is 64 bits and the
341: integer only 32. The compiler warns us of loss of precision. However we
342: know we casted a normal 32-bit integer into this pointer a few
343: microseconds earlier, so it is ok. Nothing to worry about. */
344: #define POINTER_TO_INT(p) ((int)(long)(p))
345:
346: static void float_formatter(
347: GtkTreeViewColumn * tree_column ATTRIBUTE_UNUSED,
348: GtkCellRenderer * cell,
349: GtkTreeModel * tree_model,
350: GtkTreeIter * iter,
351: gpointer data)
352: {
353: gfloat f;
354: gchar text[64];
355: gtk_tree_model_get(tree_model, iter, POINTER_TO_INT(data), &f, -1);
356: sprintf(text, "%.2f", f);
357: g_object_set(cell, "text", text, NULL);
358: }
359:
360: static void percent_formatter(
361: GtkTreeViewColumn * tree_column ATTRIBUTE_UNUSED,
362: GtkCellRenderer * cell,
363: GtkTreeModel * tree_model,
364: GtkTreeIter * iter,
365: gpointer data)
366: {
367: gfloat f;
368: gchar text[64];
369: gtk_tree_model_get(tree_model, iter, POINTER_TO_INT(data), &f, -1);
370: sprintf(text, "%.1f%%", f);
371: g_object_set(cell, "text", text, NULL);
372: }
373:
374: static void TreeViewCreate(
375: struct mtr_ctl *ctl)
376: {
377: GtkCellRenderer *renderer;
378: GtkTreeViewColumn *column;
379:
380: ReportStore = gtk_list_store_new(N_COLS,
381: #ifdef HAVE_IPINFO
382: G_TYPE_STRING,
383: #endif
384: G_TYPE_STRING,
385: G_TYPE_FLOAT,
386: G_TYPE_INT,
387: G_TYPE_INT,
388: G_TYPE_INT,
389: G_TYPE_INT,
390: G_TYPE_INT,
391: G_TYPE_INT,
392: G_TYPE_FLOAT, G_TYPE_STRING);
393:
394: ReportTreeView =
395: gtk_tree_view_new_with_model(GTK_TREE_MODEL(ReportStore));
396:
1.1.1.2 ! misho 397: g_signal_connect(G_OBJECT(ReportTreeView), "button_press_event",
1.1 misho 398: G_CALLBACK(ReportTreeView_clicked), ctl);
399:
400: #ifdef HAVE_IPINFO
401: if (is_printii(ctl)) {
402: renderer = gtk_cell_renderer_text_new();
403: column = gtk_tree_view_column_new_with_attributes("ASN",
404: renderer,
405: "text", COL_ASN,
406: "foreground",
407: COL_COLOR, NULL);
408: gtk_tree_view_column_set_resizable(column, TRUE);
409: gtk_tree_view_append_column(GTK_TREE_VIEW(ReportTreeView), column);
410: }
411: #endif
412:
413: renderer = gtk_cell_renderer_text_new();
414: column = gtk_tree_view_column_new_with_attributes("Hostname",
415: renderer,
416: "text", COL_HOSTNAME,
417: "foreground",
418: COL_COLOR, NULL);
419: gtk_tree_view_column_set_expand(column, TRUE);
420: gtk_tree_view_column_set_resizable(column, TRUE);
421: gtk_tree_view_append_column(GTK_TREE_VIEW(ReportTreeView), column);
422:
423: renderer = gtk_cell_renderer_text_new();
424: g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
425: column = gtk_tree_view_column_new_with_attributes("Loss",
426: renderer,
427: "text", COL_LOSS,
428: "foreground",
429: COL_COLOR, NULL);
430: gtk_tree_view_column_set_resizable(column, TRUE);
431: gtk_tree_view_column_set_cell_data_func(column, renderer,
432: percent_formatter,
433: (void *) COL_LOSS, NULL);
434: gtk_tree_view_append_column(GTK_TREE_VIEW(ReportTreeView), column);
435:
436: renderer = gtk_cell_renderer_text_new();
437: g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
438: column = gtk_tree_view_column_new_with_attributes("Snt",
439: renderer,
440: "text", COL_SNT,
441: "foreground",
442: COL_COLOR, NULL);
443: gtk_tree_view_column_set_resizable(column, TRUE);
444: gtk_tree_view_append_column(GTK_TREE_VIEW(ReportTreeView), column);
445:
446: renderer = gtk_cell_renderer_text_new();
447: g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
448: column = gtk_tree_view_column_new_with_attributes("Last",
449: renderer,
450: "text", COL_LAST,
451: "foreground",
452: COL_COLOR, NULL);
453: gtk_tree_view_column_set_resizable(column, TRUE);
454: gtk_tree_view_append_column(GTK_TREE_VIEW(ReportTreeView), column);
455:
456: renderer = gtk_cell_renderer_text_new();
457: g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
458: column = gtk_tree_view_column_new_with_attributes("Avg",
459: renderer,
460: "text", COL_AVG,
461: "foreground",
462: COL_COLOR, NULL);
463: gtk_tree_view_column_set_resizable(column, TRUE);
464: gtk_tree_view_append_column(GTK_TREE_VIEW(ReportTreeView), column);
465:
466: renderer = gtk_cell_renderer_text_new();
467: g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
468: column = gtk_tree_view_column_new_with_attributes("Best",
469: renderer,
470: "text", COL_BEST,
471: "foreground",
472: COL_COLOR, NULL);
473: gtk_tree_view_column_set_resizable(column, TRUE);
474: gtk_tree_view_append_column(GTK_TREE_VIEW(ReportTreeView), column);
475:
476: renderer = gtk_cell_renderer_text_new();
477: g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
478: column = gtk_tree_view_column_new_with_attributes("Worst",
479: renderer,
480: "text", COL_WORST,
481: "foreground",
482: COL_COLOR, NULL);
483: gtk_tree_view_column_set_resizable(column, TRUE);
484: gtk_tree_view_append_column(GTK_TREE_VIEW(ReportTreeView), column);
485:
486: renderer = gtk_cell_renderer_text_new();
487: g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
488: column = gtk_tree_view_column_new_with_attributes("StDev",
489: renderer,
490: "text", COL_STDEV,
491: "foreground",
492: COL_COLOR, NULL);
493: gtk_tree_view_column_set_resizable(column, TRUE);
494: gtk_tree_view_column_set_cell_data_func(column, renderer,
495: float_formatter,
496: (void *) COL_STDEV, NULL);
497: gtk_tree_view_append_column(GTK_TREE_VIEW(ReportTreeView), column);
498:
499: }
500:
501: static void update_tree_row(
502: struct mtr_ctl *ctl,
503: int row,
504: GtkTreeIter * iter)
505: {
506: ip_t *addr;
507: char str[256] = "???", *name = str;
508:
509: addr = net_addr(row);
1.1.1.2 ! misho 510: if (addrcmp(addr, &ctl->unspec_addr, ctl->af)) {
1.1 misho 511: if ((name = dns_lookup(ctl, addr))) {
512: if (ctl->show_ips) {
513: snprintf(str, sizeof(str), "%s (%s)", name,
514: strlongip(ctl, addr));
515: name = str;
516: }
517: } else
518: name = strlongip(ctl, addr);
519: }
520:
521: gtk_list_store_set(ReportStore, iter,
522: COL_HOSTNAME, name,
523: COL_LOSS, (float) (net_loss(row) / 1000.0),
524: COL_RCV, net_returned(row),
525: COL_SNT, net_xmit(row),
526: COL_LAST, net_last(row) / 1000,
527: COL_BEST, net_best(row) / 1000,
528: COL_AVG, net_avg(row) / 1000,
529: COL_WORST, net_worst(row) / 1000,
530: COL_STDEV, (float) (net_stdev(row) / 1000.0),
531: COL_COLOR, net_up(row) ? NULL : "red", -1);
532: #ifdef HAVE_IPINFO
533: if (is_printii(ctl))
534: gtk_list_store_set(ReportStore, iter, COL_ASN,
535: fmt_ipinfo(ctl, addr), -1);
536: #endif
537: }
538:
539: void gtk_redraw(
540: struct mtr_ctl *ctl)
541: {
542: int max = net_max(ctl);
543:
544: GtkTreeIter iter;
545: int row = net_min(ctl);
546: gboolean valid;
547:
548: valid =
549: gtk_tree_model_get_iter_first(GTK_TREE_MODEL(ReportStore), &iter);
550:
551: while (valid) {
552: if (row < max) {
553: update_tree_row(ctl, row++, &iter);
554: valid =
555: gtk_tree_model_iter_next(GTK_TREE_MODEL(ReportStore),
556: &iter);
557: } else {
558: valid = gtk_list_store_remove(ReportStore, &iter);
559: }
560: }
561: while (row < max) {
562: gtk_list_store_append(ReportStore, &iter);
563: update_tree_row(ctl, row++, &iter);
564: }
565: }
566:
1.1.1.2 ! misho 567: // GTK 3 has changed the interface a bit. Here a few defines so that we can
! 568: // work with GTK2 or GTK3 as required.
! 569: #ifdef HAVE_GTK3
! 570: #define gtk_vbox_new_(orientation,sz) gtk_box_new(orientation, sz)
! 571: #define gtk_hbox_new_(orientation,sz) gtk_box_new(orientation, sz)
! 572: #else
! 573: #define gtk_vbox_new_(orientation,sz) gtk_vbox_new(FALSE, sz)
! 574: #define gtk_hbox_new_(orientation,sz) gtk_hbox_new(FALSE, sz)
! 575: #endif
1.1 misho 576:
577: static void Window_fill(
578: struct mtr_ctl *ctl,
579: GtkWidget * Window)
580: {
581: GtkWidget *VBox;
582: GtkWidget *Toolbar;
583: GtkWidget *scroll;
584:
585: gtk_window_set_title(GTK_WINDOW(Window), "My traceroute");
586: gtk_window_set_default_size(GTK_WINDOW(Window), 650, 400);
587: gtk_container_set_border_width(GTK_CONTAINER(Window), 10);
588:
1.1.1.2 ! misho 589: VBox = gtk_vbox_new_(GTK_ORIENTATION_VERTICAL, 10);
! 590: Toolbar = gtk_hbox_new_(GTK_ORIENTATION_HORIZONTAL, 10);
! 591:
1.1 misho 592: Toolbar_fill(ctl, Toolbar);
593: gtk_box_pack_start(GTK_BOX(VBox), Toolbar, FALSE, FALSE, 0);
594:
595: TreeViewCreate(ctl);
596: scroll = gtk_scrolled_window_new(NULL, NULL);
597: gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
598: GTK_POLICY_AUTOMATIC,
599: GTK_POLICY_AUTOMATIC);
600: gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),
601: GTK_SHADOW_IN);
602: gtk_container_add(GTK_CONTAINER(scroll), ReportTreeView);
603: gtk_box_pack_start(GTK_BOX(VBox), scroll, TRUE, TRUE, 0);
604:
605: gtk_container_add(GTK_CONTAINER(Window), VBox);
606: }
607:
608:
609: void gtk_open(
610: struct mtr_ctl *ctl)
611: {
612: GdkPixbuf *icon;
613: int argc = 1;
614: char *args[2];
615: char **argv;
616:
617: argv = args;
618: argv[0] = xstrdup("");
619: argv[1] = NULL;
620: gtk_do_init(&argc, &argv);
621: free(argv[0]);
622:
623: icon = gdk_pixbuf_new_from_xpm_data((const char **) mtr_icon);
624: gtk_window_set_default_icon(icon);
625:
626: main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
627:
628: g_set_application_name("My traceroute");
629:
630: Window_fill(ctl, main_window);
631:
1.1.1.2 ! misho 632: g_signal_connect(G_OBJECT(main_window), "delete_event",
! 633: G_CALLBACK(Window_destroy), NULL);
! 634: g_signal_connect(G_OBJECT(main_window), "destroy",
! 635: G_CALLBACK(Window_destroy), NULL);
1.1 misho 636:
637: gtk_widget_show_all(main_window);
638: }
639:
640:
641: void gtk_close(
642: void)
643: {
644: }
645:
646:
647: int gtk_keyaction(
648: void)
649: {
650: return 0;
651: }
652:
653:
654: static gint gtk_ping(
655: gpointer data)
656: {
657: struct mtr_ctl *ctl = (struct mtr_ctl *) data;
658:
659: gtk_redraw(ctl);
660: net_send_batch(ctl);
661: net_harvest_fds(ctl);
662: g_source_remove(ping_timeout_timer);
663: gtk_add_ping_timeout(ctl);
664: return TRUE;
665: }
666:
667:
668: static gboolean gtk_net_data(
669: ATTRIBUTE_UNUSED GIOChannel * channel,
670: ATTRIBUTE_UNUSED GIOCondition cond,
671: gpointer data)
672: {
673: struct mtr_ctl *ctl = (struct mtr_ctl *) data;
674:
675: net_process_return(ctl);
676: return TRUE;
677: }
678:
679:
680: static gboolean gtk_dns_data(
681: ATTRIBUTE_UNUSED GIOChannel * channel,
682: ATTRIBUTE_UNUSED GIOCondition cond,
683: gpointer data)
684: {
685: struct mtr_ctl *ctl = (struct mtr_ctl *) data;
686:
687: dns_ack(ctl);
688: gtk_redraw(ctl);
689: return TRUE;
690: }
691:
692: #ifdef ENABLE_IPV6
693: static gboolean gtk_dns_data6(
694: ATTRIBUTE_UNUSED GIOChannel * channel,
695: ATTRIBUTE_UNUSED GIOCondition cond,
696: gpointer data)
697: {
698: struct mtr_ctl *ctl = (struct mtr_ctl *) data;
699:
700: dns_ack6();
701: gtk_redraw(ctl);
702: return TRUE;
703: }
704: #endif
705:
706:
707: void gtk_loop(
708: struct mtr_ctl *ctl)
709: {
710: GIOChannel *net_iochannel, *dns_iochannel;
711:
712: gtk_add_ping_timeout(ctl);
713:
714: net_iochannel = g_io_channel_unix_new(net_waitfd());
715: g_io_add_watch(net_iochannel, G_IO_IN, gtk_net_data, ctl);
716: #ifdef ENABLE_IPV6
717: if (dns_waitfd6() > 0) {
718: dns_iochannel = g_io_channel_unix_new(dns_waitfd6());
719: g_io_add_watch(dns_iochannel, G_IO_IN, gtk_dns_data6, ctl);
720: }
721: #endif
722: dns_iochannel = g_io_channel_unix_new(dns_waitfd());
723: g_io_add_watch(dns_iochannel, G_IO_IN, gtk_dns_data, ctl);
724:
725: gtk_main();
726: }
727:
728: static gboolean NewDestination_activate(
729: GtkWidget * widget ATTRIBUTE_UNUSED,
730: gpointer data)
731: {
732: gchar *hostname;
733: struct mtr_ctl *ctl = (struct mtr_ctl *) data;
734: GtkTreePath *path = (GtkTreePath *) ctl->gtk_data;
735:
736: hostname = getSelectedHost(path);
737: if (hostname) {
738: ctl->gtk_data = hostname;
739: gtk_entry_set_text(GTK_ENTRY(Entry), hostname);
740: Host_activate(Entry, ctl);
741: g_free(hostname);
742: }
743: return TRUE;
744: }
745:
746:
747: static gboolean Copy_activate(
748: GtkWidget * widget ATTRIBUTE_UNUSED,
749: gpointer data)
750: {
751: gchar *hostname;
752: GtkTreePath *path = (GtkTreePath *) data;
753:
754: hostname = getSelectedHost(path);
755: if (hostname != NULL) {
756: GtkClipboard *clipboard;
757:
758: clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
759: gtk_clipboard_set_text(clipboard, hostname, -1);
760:
761: clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
762: gtk_clipboard_set_text(clipboard, hostname, -1);
763:
764: g_free(hostname);
765: }
766:
767: return TRUE;
768: }
769:
770: static gchar *getSelectedHost(
771: GtkTreePath * path)
772: {
773: GtkTreeIter iter;
774: gchar *name = NULL;
775:
776: if (gtk_tree_model_get_iter(GTK_TREE_MODEL(ReportStore), &iter, path)) {
777: gtk_tree_model_get(GTK_TREE_MODEL(ReportStore), &iter,
778: COL_HOSTNAME, &name, -1);
779: }
780: gtk_tree_path_free(path);
781: return name;
782: }
783:
784:
785: static gboolean ReportTreeView_clicked(
786: GtkWidget * Tree ATTRIBUTE_UNUSED,
787: GdkEventButton * event,
788: gpointer data)
789: {
790: GtkWidget *popup_menu;
791: GtkWidget *copy_item;
792: GtkWidget *newdestination_item;
793: GtkTreePath *path;
794: struct mtr_ctl *ctl = (struct mtr_ctl *) data;
795:
796: if (event->type != GDK_BUTTON_PRESS || event->button != 3)
797: return FALSE;
798:
799: if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(ReportTreeView),
800: event->x, event->y, &path, NULL,
801: NULL, NULL))
802: return FALSE;
803:
804: gtk_tree_view_set_cursor(GTK_TREE_VIEW(ReportTreeView), path, NULL,
805: FALSE);
806:
807: /* Single right click: prepare and show the popup menu */
808: popup_menu = gtk_menu_new();
809:
810: copy_item = gtk_menu_item_new_with_label("Copy to clipboard");
811: newdestination_item =
812: gtk_menu_item_new_with_label("Set as new destination");
813:
1.1.1.2 ! misho 814: gtk_menu_shell_append(GTK_MENU_SHELL(popup_menu), copy_item);
! 815: gtk_menu_shell_append(GTK_MENU_SHELL(popup_menu), newdestination_item);
1.1 misho 816:
1.1.1.2 ! misho 817: g_signal_connect(G_OBJECT(copy_item), "activate",
! 818: G_CALLBACK(Copy_activate), path);
1.1 misho 819:
820: ctl->gtk_data = path;
1.1.1.2 ! misho 821: g_signal_connect(G_OBJECT(newdestination_item), "activate",
! 822: G_CALLBACK(NewDestination_activate), ctl);
1.1 misho 823:
824: gtk_widget_show(copy_item);
825: gtk_widget_show(newdestination_item);
826:
1.1.1.2 ! misho 827: #ifdef HAVE_GTK3
! 828: gtk_menu_popup_at_pointer(GTK_MENU(popup_menu), NULL);
! 829: #else
1.1 misho 830: gtk_menu_popup(GTK_MENU(popup_menu), NULL, NULL, NULL, NULL,
831: 0, event->time);
1.1.1.2 ! misho 832: #endif
1.1 misho 833: return TRUE;
834: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>