Annotation of embedaddon/mtr/ui/gtk.c, revision 1.1.1.1
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:
15: You should have received a copy of the GNU General Public License
16: along with this program; if not, write to the Free Software
17: Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
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",
201: "website", "http://www.bitwizard.nl/mtr/",
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:
275: Button = gtk_button_new_from_stock(GTK_STOCK_QUIT);
276: gtk_box_pack_end(GTK_BOX(Toolbar), Button, FALSE, FALSE, 0);
277: g_signal_connect(GTK_OBJECT(Button), "clicked",
278: GTK_SIGNAL_FUNC(Window_destroy), NULL);
279:
280: Button = gtk_button_new_from_stock(GTK_STOCK_ABOUT);
281: gtk_box_pack_end(GTK_BOX(Toolbar), Button, FALSE, FALSE, 0);
282: g_signal_connect(GTK_OBJECT(Button), "clicked",
283: GTK_SIGNAL_FUNC(About_clicked), NULL);
284:
285: Button = gtk_button_new_with_mnemonic("_Restart");
286: gtk_box_pack_end(GTK_BOX(Toolbar), Button, FALSE, FALSE, 0);
287: g_signal_connect(GTK_OBJECT(Button), "clicked",
288: GTK_SIGNAL_FUNC(Restart_clicked), ctl);
289:
290: Pause_Button = gtk_toggle_button_new_with_mnemonic("_Pause");
291: gtk_box_pack_end(GTK_BOX(Toolbar), Pause_Button, FALSE, FALSE, 0);
292: g_signal_connect(GTK_OBJECT(Pause_Button), "clicked",
293: GTK_SIGNAL_FUNC(Pause_clicked), ctl);
294:
295: /* allow root only to set zero delay */
296: Adjustment = (GtkAdjustment *) gtk_adjustment_new(ctl->WaitTime,
297: getuid() ==
298: 0 ? 0.01 : 1.00,
299: 999.99, 1.0, 10.0,
300: 0.0);
301: Button = gtk_spin_button_new(Adjustment, 0.5, 2);
302: gtk_spin_button_set_numeric(GTK_SPIN_BUTTON(Button), TRUE);
303: gtk_box_pack_end(GTK_BOX(Toolbar), Button, FALSE, FALSE, 0);
304: ctl->gtk_data = Button;
305: g_signal_connect(GTK_OBJECT(Adjustment), "value_changed",
306: GTK_SIGNAL_FUNC(WaitTime_changed), ctl);
307:
308: Label = gtk_label_new_with_mnemonic("_Hostname:");
309: gtk_box_pack_start(GTK_BOX(Toolbar), Label, FALSE, FALSE, 0);
310:
311: Entry = gtk_entry_new();
312: gtk_entry_set_text(GTK_ENTRY(Entry), ctl->Hostname);
313: g_signal_connect(GTK_OBJECT(Entry), "activate",
314: GTK_SIGNAL_FUNC(Host_activate), ctl);
315: gtk_box_pack_start(GTK_BOX(Toolbar), Entry, TRUE, TRUE, 0);
316:
317: gtk_label_set_mnemonic_widget(GTK_LABEL(Label), Entry);
318: }
319:
320: static GtkWidget *ReportTreeView;
321: static GtkListStore *ReportStore;
322:
323: enum {
324: #ifdef HAVE_IPINFO
325: COL_ASN,
326: #endif
327: COL_HOSTNAME,
328: COL_LOSS,
329: COL_RCV,
330: COL_SNT,
331: COL_LAST,
332: COL_BEST,
333: COL_AVG,
334: COL_WORST,
335: COL_STDEV,
336: COL_COLOR,
337: N_COLS
338: };
339:
340: /* Trick to cast a pointer to integer. We are mis-using a pointer as a
341: single integer. On 64-bit architectures, the pointer is 64 bits and the
342: integer only 32. The compiler warns us of loss of precision. However we
343: know we casted a normal 32-bit integer into this pointer a few
344: microseconds earlier, so it is ok. Nothing to worry about. */
345: #define POINTER_TO_INT(p) ((int)(long)(p))
346:
347: static void float_formatter(
348: GtkTreeViewColumn * tree_column ATTRIBUTE_UNUSED,
349: GtkCellRenderer * cell,
350: GtkTreeModel * tree_model,
351: GtkTreeIter * iter,
352: gpointer data)
353: {
354: gfloat f;
355: gchar text[64];
356: gtk_tree_model_get(tree_model, iter, POINTER_TO_INT(data), &f, -1);
357: sprintf(text, "%.2f", f);
358: g_object_set(cell, "text", text, NULL);
359: }
360:
361: static void percent_formatter(
362: GtkTreeViewColumn * tree_column ATTRIBUTE_UNUSED,
363: GtkCellRenderer * cell,
364: GtkTreeModel * tree_model,
365: GtkTreeIter * iter,
366: gpointer data)
367: {
368: gfloat f;
369: gchar text[64];
370: gtk_tree_model_get(tree_model, iter, POINTER_TO_INT(data), &f, -1);
371: sprintf(text, "%.1f%%", f);
372: g_object_set(cell, "text", text, NULL);
373: }
374:
375: static void TreeViewCreate(
376: struct mtr_ctl *ctl)
377: {
378: GtkCellRenderer *renderer;
379: GtkTreeViewColumn *column;
380:
381: ReportStore = gtk_list_store_new(N_COLS,
382: #ifdef HAVE_IPINFO
383: G_TYPE_STRING,
384: #endif
385: G_TYPE_STRING,
386: G_TYPE_FLOAT,
387: G_TYPE_INT,
388: G_TYPE_INT,
389: G_TYPE_INT,
390: G_TYPE_INT,
391: G_TYPE_INT,
392: G_TYPE_INT,
393: G_TYPE_FLOAT, G_TYPE_STRING);
394:
395: ReportTreeView =
396: gtk_tree_view_new_with_model(GTK_TREE_MODEL(ReportStore));
397:
398: g_signal_connect(GTK_OBJECT(ReportTreeView), "button_press_event",
399: G_CALLBACK(ReportTreeView_clicked), ctl);
400:
401: #ifdef HAVE_IPINFO
402: if (is_printii(ctl)) {
403: renderer = gtk_cell_renderer_text_new();
404: column = gtk_tree_view_column_new_with_attributes("ASN",
405: renderer,
406: "text", COL_ASN,
407: "foreground",
408: COL_COLOR, NULL);
409: gtk_tree_view_column_set_resizable(column, TRUE);
410: gtk_tree_view_append_column(GTK_TREE_VIEW(ReportTreeView), column);
411: }
412: #endif
413:
414: renderer = gtk_cell_renderer_text_new();
415: column = gtk_tree_view_column_new_with_attributes("Hostname",
416: renderer,
417: "text", COL_HOSTNAME,
418: "foreground",
419: COL_COLOR, NULL);
420: gtk_tree_view_column_set_expand(column, TRUE);
421: gtk_tree_view_column_set_resizable(column, TRUE);
422: gtk_tree_view_append_column(GTK_TREE_VIEW(ReportTreeView), column);
423:
424: renderer = gtk_cell_renderer_text_new();
425: g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
426: column = gtk_tree_view_column_new_with_attributes("Loss",
427: renderer,
428: "text", COL_LOSS,
429: "foreground",
430: COL_COLOR, NULL);
431: gtk_tree_view_column_set_resizable(column, TRUE);
432: gtk_tree_view_column_set_cell_data_func(column, renderer,
433: percent_formatter,
434: (void *) COL_LOSS, NULL);
435: gtk_tree_view_append_column(GTK_TREE_VIEW(ReportTreeView), column);
436:
437: renderer = gtk_cell_renderer_text_new();
438: g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
439: column = gtk_tree_view_column_new_with_attributes("Snt",
440: renderer,
441: "text", COL_SNT,
442: "foreground",
443: COL_COLOR, NULL);
444: gtk_tree_view_column_set_resizable(column, TRUE);
445: gtk_tree_view_append_column(GTK_TREE_VIEW(ReportTreeView), column);
446:
447: renderer = gtk_cell_renderer_text_new();
448: g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
449: column = gtk_tree_view_column_new_with_attributes("Last",
450: renderer,
451: "text", COL_LAST,
452: "foreground",
453: COL_COLOR, NULL);
454: gtk_tree_view_column_set_resizable(column, TRUE);
455: gtk_tree_view_append_column(GTK_TREE_VIEW(ReportTreeView), column);
456:
457: renderer = gtk_cell_renderer_text_new();
458: g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
459: column = gtk_tree_view_column_new_with_attributes("Avg",
460: renderer,
461: "text", COL_AVG,
462: "foreground",
463: COL_COLOR, NULL);
464: gtk_tree_view_column_set_resizable(column, TRUE);
465: gtk_tree_view_append_column(GTK_TREE_VIEW(ReportTreeView), column);
466:
467: renderer = gtk_cell_renderer_text_new();
468: g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
469: column = gtk_tree_view_column_new_with_attributes("Best",
470: renderer,
471: "text", COL_BEST,
472: "foreground",
473: COL_COLOR, NULL);
474: gtk_tree_view_column_set_resizable(column, TRUE);
475: gtk_tree_view_append_column(GTK_TREE_VIEW(ReportTreeView), column);
476:
477: renderer = gtk_cell_renderer_text_new();
478: g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
479: column = gtk_tree_view_column_new_with_attributes("Worst",
480: renderer,
481: "text", COL_WORST,
482: "foreground",
483: COL_COLOR, NULL);
484: gtk_tree_view_column_set_resizable(column, TRUE);
485: gtk_tree_view_append_column(GTK_TREE_VIEW(ReportTreeView), column);
486:
487: renderer = gtk_cell_renderer_text_new();
488: g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
489: column = gtk_tree_view_column_new_with_attributes("StDev",
490: renderer,
491: "text", COL_STDEV,
492: "foreground",
493: COL_COLOR, NULL);
494: gtk_tree_view_column_set_resizable(column, TRUE);
495: gtk_tree_view_column_set_cell_data_func(column, renderer,
496: float_formatter,
497: (void *) COL_STDEV, NULL);
498: gtk_tree_view_append_column(GTK_TREE_VIEW(ReportTreeView), column);
499:
500: }
501:
502: static void update_tree_row(
503: struct mtr_ctl *ctl,
504: int row,
505: GtkTreeIter * iter)
506: {
507: ip_t *addr;
508: char str[256] = "???", *name = str;
509:
510: addr = net_addr(row);
511: if (addrcmp((void *) addr, (void *) &ctl->unspec_addr, ctl->af)) {
512: if ((name = dns_lookup(ctl, addr))) {
513: if (ctl->show_ips) {
514: snprintf(str, sizeof(str), "%s (%s)", name,
515: strlongip(ctl, addr));
516: name = str;
517: }
518: } else
519: name = strlongip(ctl, addr);
520: }
521:
522: gtk_list_store_set(ReportStore, iter,
523: COL_HOSTNAME, name,
524: COL_LOSS, (float) (net_loss(row) / 1000.0),
525: COL_RCV, net_returned(row),
526: COL_SNT, net_xmit(row),
527: COL_LAST, net_last(row) / 1000,
528: COL_BEST, net_best(row) / 1000,
529: COL_AVG, net_avg(row) / 1000,
530: COL_WORST, net_worst(row) / 1000,
531: COL_STDEV, (float) (net_stdev(row) / 1000.0),
532: COL_COLOR, net_up(row) ? NULL : "red", -1);
533: #ifdef HAVE_IPINFO
534: if (is_printii(ctl))
535: gtk_list_store_set(ReportStore, iter, COL_ASN,
536: fmt_ipinfo(ctl, addr), -1);
537: #endif
538: }
539:
540: void gtk_redraw(
541: struct mtr_ctl *ctl)
542: {
543: int max = net_max(ctl);
544:
545: GtkTreeIter iter;
546: int row = net_min(ctl);
547: gboolean valid;
548:
549: valid =
550: gtk_tree_model_get_iter_first(GTK_TREE_MODEL(ReportStore), &iter);
551:
552: while (valid) {
553: if (row < max) {
554: update_tree_row(ctl, row++, &iter);
555: valid =
556: gtk_tree_model_iter_next(GTK_TREE_MODEL(ReportStore),
557: &iter);
558: } else {
559: valid = gtk_list_store_remove(ReportStore, &iter);
560: }
561: }
562: while (row < max) {
563: gtk_list_store_append(ReportStore, &iter);
564: update_tree_row(ctl, row++, &iter);
565: }
566: }
567:
568:
569: static void Window_fill(
570: struct mtr_ctl *ctl,
571: GtkWidget * Window)
572: {
573: GtkWidget *VBox;
574: GtkWidget *Toolbar;
575: GtkWidget *scroll;
576:
577: gtk_window_set_title(GTK_WINDOW(Window), "My traceroute");
578: gtk_window_set_default_size(GTK_WINDOW(Window), 650, 400);
579: gtk_container_set_border_width(GTK_CONTAINER(Window), 10);
580: VBox = gtk_vbox_new(FALSE, 10);
581:
582: Toolbar = gtk_hbox_new(FALSE, 10);
583: Toolbar_fill(ctl, Toolbar);
584: gtk_box_pack_start(GTK_BOX(VBox), Toolbar, FALSE, FALSE, 0);
585:
586: TreeViewCreate(ctl);
587: scroll = gtk_scrolled_window_new(NULL, NULL);
588: gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
589: GTK_POLICY_AUTOMATIC,
590: GTK_POLICY_AUTOMATIC);
591: gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scroll),
592: GTK_SHADOW_IN);
593: gtk_container_add(GTK_CONTAINER(scroll), ReportTreeView);
594: gtk_box_pack_start(GTK_BOX(VBox), scroll, TRUE, TRUE, 0);
595:
596: gtk_container_add(GTK_CONTAINER(Window), VBox);
597: }
598:
599:
600: void gtk_open(
601: struct mtr_ctl *ctl)
602: {
603: GdkPixbuf *icon;
604: int argc = 1;
605: char *args[2];
606: char **argv;
607:
608: argv = args;
609: argv[0] = xstrdup("");
610: argv[1] = NULL;
611: gtk_do_init(&argc, &argv);
612: free(argv[0]);
613:
614: icon = gdk_pixbuf_new_from_xpm_data((const char **) mtr_icon);
615: gtk_window_set_default_icon(icon);
616:
617: main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
618:
619: g_set_application_name("My traceroute");
620:
621: Window_fill(ctl, main_window);
622:
623: g_signal_connect(GTK_OBJECT(main_window), "delete_event",
624: GTK_SIGNAL_FUNC(Window_destroy), NULL);
625: g_signal_connect(GTK_OBJECT(main_window), "destroy",
626: GTK_SIGNAL_FUNC(Window_destroy), NULL);
627:
628: gtk_widget_show_all(main_window);
629: }
630:
631:
632: void gtk_close(
633: void)
634: {
635: }
636:
637:
638: int gtk_keyaction(
639: void)
640: {
641: return 0;
642: }
643:
644:
645: static gint gtk_ping(
646: gpointer data)
647: {
648: struct mtr_ctl *ctl = (struct mtr_ctl *) data;
649:
650: gtk_redraw(ctl);
651: net_send_batch(ctl);
652: net_harvest_fds(ctl);
653: g_source_remove(ping_timeout_timer);
654: gtk_add_ping_timeout(ctl);
655: return TRUE;
656: }
657:
658:
659: static gboolean gtk_net_data(
660: ATTRIBUTE_UNUSED GIOChannel * channel,
661: ATTRIBUTE_UNUSED GIOCondition cond,
662: gpointer data)
663: {
664: struct mtr_ctl *ctl = (struct mtr_ctl *) data;
665:
666: net_process_return(ctl);
667: return TRUE;
668: }
669:
670:
671: static gboolean gtk_dns_data(
672: ATTRIBUTE_UNUSED GIOChannel * channel,
673: ATTRIBUTE_UNUSED GIOCondition cond,
674: gpointer data)
675: {
676: struct mtr_ctl *ctl = (struct mtr_ctl *) data;
677:
678: dns_ack(ctl);
679: gtk_redraw(ctl);
680: return TRUE;
681: }
682:
683: #ifdef ENABLE_IPV6
684: static gboolean gtk_dns_data6(
685: ATTRIBUTE_UNUSED GIOChannel * channel,
686: ATTRIBUTE_UNUSED GIOCondition cond,
687: gpointer data)
688: {
689: struct mtr_ctl *ctl = (struct mtr_ctl *) data;
690:
691: dns_ack6();
692: gtk_redraw(ctl);
693: return TRUE;
694: }
695: #endif
696:
697:
698: void gtk_loop(
699: struct mtr_ctl *ctl)
700: {
701: GIOChannel *net_iochannel, *dns_iochannel;
702:
703: gtk_add_ping_timeout(ctl);
704:
705: net_iochannel = g_io_channel_unix_new(net_waitfd());
706: g_io_add_watch(net_iochannel, G_IO_IN, gtk_net_data, ctl);
707: #ifdef ENABLE_IPV6
708: if (dns_waitfd6() > 0) {
709: dns_iochannel = g_io_channel_unix_new(dns_waitfd6());
710: g_io_add_watch(dns_iochannel, G_IO_IN, gtk_dns_data6, ctl);
711: }
712: #endif
713: dns_iochannel = g_io_channel_unix_new(dns_waitfd());
714: g_io_add_watch(dns_iochannel, G_IO_IN, gtk_dns_data, ctl);
715:
716: gtk_main();
717: }
718:
719: static gboolean NewDestination_activate(
720: GtkWidget * widget ATTRIBUTE_UNUSED,
721: gpointer data)
722: {
723: gchar *hostname;
724: struct mtr_ctl *ctl = (struct mtr_ctl *) data;
725: GtkTreePath *path = (GtkTreePath *) ctl->gtk_data;
726:
727: hostname = getSelectedHost(path);
728: if (hostname) {
729: ctl->gtk_data = hostname;
730: gtk_entry_set_text(GTK_ENTRY(Entry), hostname);
731: Host_activate(Entry, ctl);
732: g_free(hostname);
733: }
734: return TRUE;
735: }
736:
737:
738: static gboolean Copy_activate(
739: GtkWidget * widget ATTRIBUTE_UNUSED,
740: gpointer data)
741: {
742: gchar *hostname;
743: GtkTreePath *path = (GtkTreePath *) data;
744:
745: hostname = getSelectedHost(path);
746: if (hostname != NULL) {
747: GtkClipboard *clipboard;
748:
749: clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
750: gtk_clipboard_set_text(clipboard, hostname, -1);
751:
752: clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
753: gtk_clipboard_set_text(clipboard, hostname, -1);
754:
755: g_free(hostname);
756: }
757:
758: return TRUE;
759: }
760:
761: static gchar *getSelectedHost(
762: GtkTreePath * path)
763: {
764: GtkTreeIter iter;
765: gchar *name = NULL;
766:
767: if (gtk_tree_model_get_iter(GTK_TREE_MODEL(ReportStore), &iter, path)) {
768: gtk_tree_model_get(GTK_TREE_MODEL(ReportStore), &iter,
769: COL_HOSTNAME, &name, -1);
770: }
771: gtk_tree_path_free(path);
772: return name;
773: }
774:
775:
776: static gboolean ReportTreeView_clicked(
777: GtkWidget * Tree ATTRIBUTE_UNUSED,
778: GdkEventButton * event,
779: gpointer data)
780: {
781: GtkWidget *popup_menu;
782: GtkWidget *copy_item;
783: GtkWidget *newdestination_item;
784: GtkTreePath *path;
785: struct mtr_ctl *ctl = (struct mtr_ctl *) data;
786:
787: if (event->type != GDK_BUTTON_PRESS || event->button != 3)
788: return FALSE;
789:
790: if (!gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(ReportTreeView),
791: event->x, event->y, &path, NULL,
792: NULL, NULL))
793: return FALSE;
794:
795: gtk_tree_view_set_cursor(GTK_TREE_VIEW(ReportTreeView), path, NULL,
796: FALSE);
797:
798: /* Single right click: prepare and show the popup menu */
799: popup_menu = gtk_menu_new();
800:
801: copy_item = gtk_menu_item_new_with_label("Copy to clipboard");
802: newdestination_item =
803: gtk_menu_item_new_with_label("Set as new destination");
804:
805: gtk_menu_append(GTK_MENU(popup_menu), copy_item);
806: gtk_menu_append(GTK_MENU(popup_menu), newdestination_item);
807:
808: g_signal_connect(GTK_OBJECT(copy_item), "activate",
809: GTK_SIGNAL_FUNC(Copy_activate), path);
810:
811: ctl->gtk_data = path;
812: g_signal_connect(GTK_OBJECT(newdestination_item), "activate",
813: GTK_SIGNAL_FUNC(NewDestination_activate), ctl);
814:
815: gtk_widget_show(copy_item);
816: gtk_widget_show(newdestination_item);
817:
818: gtk_menu_popup(GTK_MENU(popup_menu), NULL, NULL, NULL, NULL,
819: 0, event->time);
820: return TRUE;
821: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>