/* $Id: main.c,v 1.22 2003/03/25 00:30:47 d3august Exp $ */ /* xtraceroute - graphically show traceroute information. * Copyright (C) 1996-1998 Björn Augustsson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "xt.h" #include #include #include #include #include #include #include #include #include #include #include // for sigaction #include // for waitpid #include // for close #include // for atoi #include "trackball.h" GtkWidget *clist; /* The list of sites below the globe */ GdkPixbuf *earth_texture; GdkPixbuf *night_texture; GdkPixbuf *created_texture; int xbegin, ybegin, zbegin; int xbeginstrafe, ybeginstrafe; GLfloat zoom = EARTH_SIZE; float curquat[4]; GLint vp[4]; /* viewport */ int newModel = 1; /* Do we need to recalc the modelview matrix? */ site local; /* For resolving where localhost is (to rotate earth right) */ static const char* versionstring = N_("Xtraceroute version "); /** * Called to reset the sites. */ static void clear_sites(void) { int i; for(i=0;iallocation.height - y, 1, 1, vp); // Note: This code is common to some in reshape. Maybe look into that. aspect = (float)glarea->allocation.width / (float)glarea->allocation.height; if(glarea->allocation.width > glarea->allocation.height) { /* It's wide. */ gluPerspective( 40.0, /* Field of view, in "y" direction. */ aspect, /* Aspect ratio */ 1.0, /* Z near */ 10.0); /* Z far */ } else { /* It's high. */ gluPerspective( 40.0/aspect, /* Field of view, in "y" direction. */ aspect, /* Aspect ratio */ 1.0, /* Z near */ 10.0); /* Z far */ } glMatrixMode(GL_MODELVIEW); set_render_mode(SELECT_MODE); makeearth(); set_render_mode(NORMAL_MODE); glCallList(WORLD); hits = glRenderMode(GL_RENDER); /* Change back to projection here to pop my old matrix. Otherwise we'll look at the world through the pickmatrix in the future. */ glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); /* Process the hits, find the closest one */ { GLuint closest_distance = ~0U; // A really big number int retval = -1; int i; for(i=0 ; iallocation.width; h = wi->allocation.height; aspect = (float)w / (float)h; glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glPopMatrix(); glLoadIdentity(); if(w > h) { /* It's wide. */ gluPerspective( 40.0, /* Field of view, in "y" direction. */ aspect, /* Aspect ratio */ 1.0, /* Z near */ 10.0); /* Z far */ } else { /* It's high. */ gluPerspective( 40.0/aspect, /* Field of view, in "y" direction. */ aspect, /* Aspect ratio */ 1.0, /* Z near */ 10.0); /* Z far */ } glPushMatrix(); glMatrixMode(GL_MODELVIEW); glGetIntegerv(GL_VIEWPORT, vp); gtk_gl_area_swapbuffers(GTK_GL_AREA(wi)); } // have these un-globalized or put in a separate file later gint rotation_track_tag = 0; gint translation_track_tag = 0; gint zoom_track_tag = 0; /** * mouse_motion(): Called when the mouse moves. (Callback.) */ static gint mouse_motion(GtkWidget *wi, GdkEventMotion *ev) { gboolean any_change = FALSE; gboolean zoom_change = FALSE; float tempquat[4]; gint x, y, w, h; gfloat strafex,strafey; GdkModifierType mods; gdk_window_get_pointer (wi->window, &x, &y, &mods); w = glarea->allocation.width; h = glarea->allocation.height; // Begin zoom stuff, sort this out later. // if (zoom_track_tag && (zbegin > -1) ) if (zoom_track_tag && (y != zbegin) ) { double fakt; fakt = (1.0 - (2*(y-zbegin) / (double)h)); // printf("fact: %f, zoom: %f\n", fakt, zoom); zoom*=fakt; if (zoom > Z_OF_EYE - EARTH_SIZE) zoom=Z_OF_EYE - EARTH_SIZE; /* prevents going inside the object */ if (zoom <= EARTH_SIZE/4) zoom = EARTH_SIZE/4; /* prevents going beyond "infinity" */ zbegin=y; any_change = TRUE; zoom_change = TRUE; } /* Rotation: */ /* This stuff doesn't really work well unless the canvas is quadratic. */ if(rotation_track_tag && (x != xbegin || y != ybegin) ) { trackball(tempquat, (2.0*xbegin - w) / w, (h - 2.0*ybegin) / h, (2.0*x - w) / w, (h - 2.0*y) / h); add_quats(tempquat, curquat, curquat); xbegin = x; ybegin = y; any_change = TRUE; } /* Translation: */ if(translation_track_tag && (x != xbeginstrafe || y != ybeginstrafe) ) { /* Modify factor 4 to get a lower/higher sensitivity. */ strafex = (gfloat) -4*(xbeginstrafe-x)/w; strafey = (gfloat) 4*(ybeginstrafe-y)/h; set_view_motion(strafex,strafey); xbeginstrafe = x; ybeginstrafe = y; any_change = TRUE; } if(zoom_change == TRUE) { /* We need to rebuild the earth in this case or the sites and lines would not be in proportion to the earth any more. */ newModel = 1; makeearth(); } else if(any_change == TRUE) { newModel = 1; redraw(glarea, NULL); } return TRUE; } /** * Called whenever any mouse button is pressed on the glarea. */ static gint mouse_button_down(GtkWidget *wi, GdkEventButton *ev) { int site; int x = (int)ev->x; int y = (int)ev->y; switch(ev->button) { case 1: /* Left button, rotation or selection */ // printf("Mouse 1 down!\n"); /* In case the user is trying to select a site */ site = which_site(x, y); if(site != -1) { gtk_clist_select_row(GTK_CLIST(clist), site, 1); /* Scroll the list here to make sure the listitem is visible in the window. */ gtk_clist_moveto(GTK_CLIST(clist),site, 1, 0.5, 0.5); break; } /* He wasn't, he's trying to rotate the earth. */ //printf("no site!\n"); xbegin = x; ybegin = y; /*vafan ? FIXME*/ zbegin = -1; if(rotation_track_tag == 0) /* To prevent duplicate callbacks. */ rotation_track_tag = gtk_idle_add((GtkFunction)mouse_motion, wi); break; case 2: /* Middle button, translation */ // printf("Mouse 2 down!\n"); xbeginstrafe = x; ybeginstrafe = y; if(translation_track_tag == 0) /* To prevent duplicate callbacks. */ translation_track_tag = gtk_idle_add((GtkFunction)mouse_motion, wi); break; case 3: /* Right button, zoom */ // printf("Mouse 3 down!\n"); zbegin = y; if(zoom_track_tag == 0) /* To prevent duplicate callbacks. */ zoom_track_tag = gtk_idle_add((GtkFunction)mouse_motion, wi); break; } return TRUE; } /*-------------------------------------------------------------------------*/ /* mouse_button_up(): Called when any mouse button gets released. */ /*-------------------------------------------------------------------------*/ static gint mouse_button_up(GtkWidget *wi, GdkEventButton *ev) { if(ev->button == 1 && rotation_track_tag != 0) { gtk_idle_remove(rotation_track_tag); rotation_track_tag = 0; // printf("Mouse 1 up!\n"); } if(ev->button == 2 && translation_track_tag != 0) { gtk_idle_remove(translation_track_tag); translation_track_tag = 0; // printf("Mouse 2 up!\n"); } if(ev->button == 3 && zoom_track_tag != 0) { gtk_idle_remove(zoom_track_tag); zoom_track_tag = 0; // printf("Mouse 3 up!\n"); } return TRUE; } /** * Makes sure the binary we're going to use is really there. */ static void lookfor(const char* const program) { struct stat statbuf; if(stat(program, &statbuf) < 0) { char tmp[501]; g_snprintf(tmp,500,_("Can't find a binary I need!\n" "I'm looking for \"%s\"."),program); perror(tmp); exit(EXIT_FAILURE); } if(! (statbuf.st_mode & S_IFREG) ) { printf(_("Problem looking for \"%s\"! It isn't a regular file!\n") ,program); exit(EXIT_FAILURE); } if(! (statbuf.st_mode & S_IXUSR) ) { printf(_("%s isn't executable!\n"),program); exit(EXIT_FAILURE); } DPRINTF("%s\t is there and looks OK.\n", program); } /** * exit_program(): Guess? */ static void exit_program(GtkWidget *wi, gpointer *data) { extern traceroute_state_t traceroute_state; // From extprog.c if(traceroute_state.fd[0] != -1) { close(traceroute_state.fd[0]); } gtk_main_quit(); } /** * about_program() */ void about_program(GtkWidget *wi, gpointer *data) { char mess[501]; g_snprintf(mess,500,_("%s%s\nBy Björn Augustsson (d3august@dtek.chalmers.se)\n" "Homepage: http://www.dtek.chalmers.se/~d3august/xt\n") ,_(versionstring),VERSION); tell_user(mess); } /*-------------------------------------------------------------------------*/ /* clist_item_selected() */ /*-------------------------------------------------------------------------*/ static gint clist_item_selected(GtkCList *parentlist, gint row, gint column, GdkEventButton *event) { if (event && event->type == GDK_2BUTTON_PRESS) { info_window(row); /* Double click detected */ } else { int i; if(sites[row].selected == 0) { /* Clear all other sites (so two sites can't be selected) */ for(i=0;ilighting_mode == (lighting_t)action) { // It wasn't a change. return; } user_settings->lighting_mode = (lighting_t)action; if (user_settings->lighting_mode == DAY_AND_NIGHT) { composite_texture(night_texture, earth_texture, &created_texture); map_texture(created_texture); } else map_texture(earth_texture); } /*-------------------------------------------------------------------------*/ /* menu_items[] : The menu struct */ /*-------------------------------------------------------------------------*/ /*-------------------------------------------------------------------------*/ /* build_menu() */ /*-------------------------------------------------------------------------*/ static void build_menu( GtkWidget *window, GtkWidget **menubar ) { /* strings are marked with a preceding N_() for translation purpose see gettext() */ const GtkItemFactoryEntry menu_items[] = { { N_("/_File"), NULL, NULL, 0, "" }, // { N_("/File/_New..."), "O", (GtkItemFactoryCallback)new_trace, 0, NULL }, { "/File/sep1", NULL, NULL, 0, "" }, { N_("/File/_Quit"), "Q", exit_program, 0, NULL }, { N_("/_Preferences"), NULL, NULL, 0, "" }, { N_("/Preferences/_Lighting"), NULL, NULL, 0, "" }, { N_("/Preferences/Lighting/Day and _night"),NULL, set_lighting_mode, DAY_AND_NIGHT, "" }, { N_("/Preferences/Lighting/_Day only"), NULL, set_lighting_mode, DAY_ONLY, "/Preferences/Lighting/Day and night" }, { N_("/_Database"), NULL, NULL, 0, "" }, { N_("/Database/Add host..."), NULL, addHost, 0, NULL }, { N_("/Database/Add net..."), NULL, addNet, 0, NULL }, { N_("/Database/Add keyword..."), NULL, addGen, 0, NULL }, /* { N_("/_Map"), NULL, NULL, 0, "" }, { N_("/Map/Map one"), NULL, choose_map, 0, NULL }, { N_("/Map/Map two"), NULL, choose_map, 1, NULL }, { N_("/Map/Map three"), NULL, choose_map, 2, NULL }, */ { N_("/_View"), NULL, NULL, 0, "" }, { N_("/View/Zoom _In"), "i", choose_zoom, 0, NULL }, { N_("/View/Zoom _Out"), "o", choose_zoom, 1, NULL }, { N_("/View/Zoom In x2"), NULL, choose_zoom, 2, NULL }, { N_("/View/Zoom Out x2"), NULL, choose_zoom, 3, NULL }, { N_("/View/Restore default"), NULL, choose_zoom, 4, NULL }, { N_("/_Transparency"), NULL, NULL, 0, "" }, { N_("/Transparency/Off"), NULL, choose_transparency, 0, NULL }, { N_("/Transparency/On"), NULL, choose_transparency, 1, NULL }, { N_("/_Help"), NULL, NULL, 0, "" }, { N_("/_Help/About"), NULL, about_program, 0, NULL }, }; gint nmenu_items = sizeof (menu_items) / sizeof (menu_items[0]); GtkAccelGroup *accel_group = gtk_accel_group_new (); GtkItemFactory *item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "
", accel_group); gtk_item_factory_create_items (item_factory, nmenu_items, menu_items, NULL); /* Set the preferences */ switch(user_settings->lighting_mode) { case DAY_AND_NIGHT: gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM ( gtk_item_factory_get_item (item_factory, "/Preferences/Lighting/Day and night")), TRUE); break; case DAY_ONLY: gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM( gtk_item_factory_get_item (item_factory, "/Preferences/Lighting/Day only")), TRUE); break; default: printf("Funky lighting mode making menu: %d\n", user_settings->lighting_mode); exit(EXIT_FAILURE); } /* Attach the new accelerator group to the window. */ gtk_window_add_accel_group (GTK_WINDOW (window), accel_group); if (menubar) *menubar = gtk_item_factory_get_widget (item_factory, "
"); } /** * Usage... */ static void usage(void) { g_print(_("%s%s\n\n" "Usage: xtraceroute [--texture <(day) texture file>]\n" " [--ntexture ]\n" " [--night | --nonight]\n" " [--lod ]\n" " [--stdin | - | ]\n" " [--version]\n" " [--help]\n") ,_(versionstring), VERSION); } /** * Handler for SIGCHLD. We get these when the extprogs die. */ static void childhandler(int sig, siginfo_t* sip, void* uap) { pid_t pid = sip->si_pid; extern traceroute_state_t traceroute_state; // DPRINTF("pid: %ld died\n", (long)pid); if(traceroute_state.pid == pid) { traceroute_state.pid = 0; spinner_unref("traceroute"); } else // It was a host process that died. { int i; for(i=0 ; i 10) { char* item_to_remove = g_list_nth_data(items, 10); items = g_list_remove(items, item_to_remove); free(item_to_remove); } gtk_combo_set_popdown_strings (GTK_COMBO(combo), items); } #if 0 /** * Callback function for the combo widget in the top of the screen. * When you click on an entry in the history there, this gets called. * It starts a new trace and adds the name to the history (again). * * The "list" argument is the gtk_list part of the combo widget that * caused the signal, and "member" is the entry in the list. * * The "combo" argument is the combo widget itself. * * See also combo_entry_callback(). */ static gint combo_list_callback(GtkWidget *list, GtkWidget *member, gpointer *combo) { char* data; gtk_label_get(GTK_LABEL(GTK_BIN(member)->child), &data); DPRINTF("combo_list_callback called. member: %s\n", data); if(!strcmp(user_settings->current_target, data)) { // combo_add_to_history(data, GTK_COMBO(combo)); } return(TRUE); } static gint change_callback(GtkWidget *list, GtkWidget *member, gpointer *combo) { printf("change\n"); return(TRUE);} static gint combochange_callback(GtkWidget *list, GtkWidget *member, gpointer *combo) { printf("combo change\n"); return(TRUE);} static gint hide_callback(GtkWidget *list, GtkWidget *member, gpointer *combo) { printf("hide\n"); return(TRUE);} #endif //0 /** * Callback function for the combo widget in the top of the screen. * When you press enter there, this gets called. It starts a new * trace and adds the name to the history. * * The "entry" argument is the gtk_entry part of the combo widget that * caused the signal. * * The "combo" argument is the combo widget itself. * * See also combo_list_callback(). */ static gint combo_entry_callback(GtkWidget *entry, gpointer *combo) { int i; extern traceroute_state_t traceroute_state; // From extprog.c /* Might make sense to have a better test for hostname validity here. Nasty things might happen if there's an ampersand character in the string for example, since it gets passed to the shell via popen(). God I'm happy this won't run SUID root... :) */ /* Maybe tell_user here or something? */ /* if(strlen(user_settings->current_target) < 2) { new_trace(NULL, NULL); return TRUE; } */ combo_add_to_history(gtk_entry_get_text(GTK_ENTRY(entry)), GTK_COMBO(combo)); strcpy(user_settings->current_target, gtk_entry_get_text(GTK_ENTRY(entry))); if(traceroute_state.fd[0] != -1) { gdk_input_remove(traceroute_state.tag); // Should this be here? Think so. close(traceroute_state.fd[0]); traceroute_state.fd[0] = -1; } traceroute_state.buffer_counter = 0; for(i=0;ilighting_mode = DAY_AND_NIGHT; has_night = TRUE; } else if(!strcmp(argv[i],"--no-night")) { if(has_night == TRUE) { g_print("Both --night and --no-night specified!\n"); usage(); exit(EXIT_FAILURE); } user_settings->lighting_mode = DAY_ONLY; has_nonight = TRUE; } else if(!strcasecmp(argv[i], "--stdin") || !strcmp(argv[i], "-")) { has_target = TRUE; strcpy(user_settings->current_target, "-"); } else if(!strcasecmp(argv[i],"--LOD")) { set_sphere_lod(atoi(argv[++i])); } else // Has to be a site to traceroute or a bad option. { if(argv[i][0] == '-') { printf("Unknown option \"%s\"\n", argv[i]); usage(); exit(EXIT_FAILURE); } else if(has_target == TRUE) { printf("More than one site argument detected!\n"); usage(); exit(EXIT_FAILURE); } strcpy(user_settings->current_target, argv[i]); has_target = TRUE; } } } /** * main() */ int main(int argc, char **argv) { GtkWidget *window; GtkWidget *vbox; GtkWidget *hbox; GtkWidget *vbox2; GtkWidget *pane; GtkWidget *spinner; GtkWidget *info_button; GtkWidget *menubar; GtkWidget *notebook; GtkWidget *label; GtkWidget *combo_hbox; GtkWidget *combo_label; GtkWidget *combo; #ifdef GTK_HAVE_FEATURES_1_1_5 GtkWidget *dummyscrwin; #endif const char *titles[] = { N_("Nr"), N_("Hostname"), N_("IP number") }; static int translated; #ifdef ENABLE_NLS setlocale (LC_ALL, ""); bindtextdomain(PACKAGE, LOCALEDIR); textdomain(PACKAGE); #endif gtk_init(&argc, &argv); if (gdk_gl_query() == FALSE) { g_print(_("OpenGL not supported\n")); exit(EXIT_FAILURE); } if (!(user_settings = (user_settings_t *)malloc(sizeof (user_settings_t)))) { perror("Alloc space for settings"); exit(EXIT_FAILURE); } /* These are the default settings. */ user_settings->lighting_mode = DAY_ONLY; strcpy(user_settings->day_texname, DATADIR "/earth.png"); strcpy(user_settings->night_texname, DATADIR "/night.png"); user_settings->LOD = 3; strcpy(user_settings->current_target,""); /* Handle loading rc file or similar in the future here. */ parse_commandline(argc, argv); if(strlen(user_settings->current_target) > 0) { calltrace(); } lookfor(TRACEPGM); // The "traceroute" binary. lookfor(HOSTPGM); // The "host" binary. lookfor(DATADIR "/xtraceroute-resolve-location.sh"); // the DNS helper earth_texture = readTexture(user_settings->day_texname); night_texture = readTexture(user_settings->night_texname); /* Edouard DBs[USER][ HOSTS ] = readHostDB("user_hosts.cache"); DBs[USER][ NETS ] = readNetDB ("user_networks.cache"); DBs[USER][GENERIC] = readGenDB ("user_generic.cache"); DBs[SITE][ HOSTS ] = readHostDB("site_hosts.cache"); DBs[SITE][ NETS ] = readNetDB ("site_networks.cache"); DBs[SITE][GENERIC] = readGenDB ("site_generic.cache"); DBs[GLOBAL][ HOSTS ] = readHostDB("hosts.cache"); DBs[GLOBAL][ NETS ] = readNetDB ("networks.cache"); DBs[GLOBAL][GENERIC] = readGenDB ("generic.cache"); */ ndg_hosts = readHostDB("hosts.cache"); local_site_hosts = readHostDB("site_hosts.cache"); local_user_hosts = readHostDB("user_hosts.cache"); // writeHostDB(ndg_hosts, "/usr/scratch/host_apa"); ndg_nets = readNetDB("networks.cache"); local_site_nets = readNetDB("site_networks.cache"); local_user_nets = readNetDB("user_networks.cache"); // local_site_generic = readGenDB("generic.cache"); // local_site_generic = readGenDB("site_generic.cache"); local_user_generic = readGenDB("user_generic.cache"); /* Set up a signal handler for reaping dead children. */ { struct sigaction sig; sig.sa_sigaction = childhandler; sig.sa_flags = SA_SIGINFO; sigaction(SIGCHLD, &sig, NULL); } internal = init_internal_db(); clear_sites(); #ifdef XT_DEBUG { extern const int n_countries; DPRINTF(_("Known countries: %d\n" "Built-in database: %d\n"), n_countries, internal->n_entries); } #endif /* XT_DEBUG */ // writeNetDB(ndg_nets, "/usr/scratch/net_apa"); trackball(curquat, 0.0, 0.0, 0.0, 0.0); // Set initial orientation of the globe. { float tmpquat[4]; float vect[3] = {0.0, 1.0, 0.0}; char namebuf[256]; struct hostent* he = NULL; struct in_addr in; memset(&local, 0, sizeof(site)); if (gethostname(namebuf,256) == 0) he = gethostbyname(namebuf); if(!he) { printf("Error gethostbynaming local hostname"); printf("(Not connected to network?)\n"); } else { memcpy(&in.s_addr, *(he->h_addr_list), sizeof(in.s_addr)); sprintf(local.ip, "%s", inet_ntoa(in)); resolve(&local); /* resolve starts a subprocess to look at DNS-LOC. Must block here until it completes. */ while (local.extpipe_active != FALSE) { // printf("."); get_from_extDNS(&local, local.extpipe[0], GDK_INPUT_READ|SYNCH_RESOLV); } DPRINTF("resolved localhost.\n"); if(local.accuracy == ACC_NONE) { printf("TIP:\tTo get xtraceroute to show your location centered on the globe\n" "\twhen it starts up, add information about this host,\n" "\t(%s) or your whole net.\n", local.name); printf("\n\tOR, even better, make your sysadmin add a LOC record to the DNS.\n" "\tThat way it will work for everyone else as well. Plus he gets to do\n" "\tthe work instead of you! See the README file for more info on this.\n"); } else { /* Rotate the globe to show the users' home. */ axis_to_quat(vect, torad*local.lon, tmpquat); add_quats(tmpquat, curquat, curquat); /* This is to do the "Y-axis", or latitude as well. */ vect[0] = 1.0; vect[1] = 0.0; vect[2] = 0.0; axis_to_quat(vect, torad*-local.lat, tmpquat); add_quats(tmpquat, curquat, curquat); } } } window = gtk_window_new(GTK_WINDOW_TOPLEVEL); { char tmp[501]; g_snprintf(tmp,500,"%s%s",_(versionstring),VERSION); gtk_window_set_title (GTK_WINDOW (window), tmp); } gtk_window_set_default_size ((GtkWindow *)window, DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT); gtk_window_set_policy ((GtkWindow *)window, FALSE, // allow shrink TRUE, // allow_grow FALSE // auto_shrink ); combo_hbox = gtk_hbox_new(FALSE, 0); combo_label = gtk_label_new (_("Target: ")); combo = gtk_combo_new (); gtk_combo_disable_activate (GTK_COMBO(combo)); gtk_signal_connect(GTK_OBJECT (GTK_COMBO(combo)->entry), "activate", GTK_SIGNAL_FUNC (combo_entry_callback), GTK_COMBO(combo)); /* OK, this _really_ sucks. What I want from my combo widget: A) When you type something into it and press enter, I get to know what you wrote. B) When you select something from the dropdown, I get to know which one it was. This one gets called for each keypress or any other activity in the entry part, which means it gets called if you move among the entries in the list w/ arrow keys. gtk_signal_connect(GTK_OBJECT (GTK_COMBO(combo)->entry), "changed", GTK_SIGNAL_FUNC (change_callback), GTK_COMBO(combo)); This is a trick to get to know when the dropdown list closes. Unfortunatly I can't tell if you chose an item, or just "closed the list". Useless. gtk_signal_connect(GTK_OBJECT (GTK_COMBO(combo)->popwin), "hide", GTK_SIGNAL_FUNC (hide_callback), GTK_COMBO(combo)); This gets called when a list entry gets selected. (clicked) Or when you move around in the list with the arrow keys. Sigh. gtk_signal_connect(GTK_OBJECT (GTK_COMBO(combo)->list), "select-child", GTK_SIGNAL_FUNC(combo_list_callback), GTK_COMBO(combo)); */ if(strlen(user_settings->current_target) > 2) { /* If we got a site to trace from the command line, add it to the history. */ combo_add_to_history(user_settings->current_target, GTK_COMBO(combo)); } gtk_box_pack_start(GTK_BOX(combo_hbox), combo_label, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(combo_hbox), combo, TRUE, TRUE, 0); /* FIXME Fix this to work with pseudocolor visuals. */ { int attribs[] = {GDK_GL_RGBA, GDK_GL_RED_SIZE,1, GDK_GL_GREEN_SIZE,1, GDK_GL_BLUE_SIZE,1, GDK_GL_DEPTH_SIZE,1, GDK_GL_DOUBLEBUFFER, GDK_GL_NONE}; glarea = gtk_gl_area_new(attribs); } vbox = gtk_vbox_new(FALSE,0); pane = gtk_vpaned_new(); // FIXME where the hell did this weird code come from? if (!translated){ int i; for (i = 0; i < 3; i++) titles [i] = _(titles [i]); translated = 1; } /* This causes a warning, because GTK is broken. */ clist = gtk_clist_new_with_titles(3, titles); gtk_clist_set_selection_mode((GtkCList *)clist, GTK_SELECTION_BROWSE); /* FIXME: These should probably be font-dependent. */ gtk_clist_set_column_width (GTK_CLIST(clist), 0, 20); gtk_clist_set_column_width (GTK_CLIST(clist), 1, 230); gtk_clist_set_column_width (GTK_CLIST(clist), 2, 100); gtk_signal_connect(GTK_OBJECT(clist), "select_row", GTK_SIGNAL_FUNC(clist_item_selected), NULL); gtk_signal_connect (GTK_OBJECT (window), "destroy", GTK_SIGNAL_FUNC(exit_program), NULL); gtk_widget_set_usize(GTK_WIDGET(glarea), DEFAULT_WINDOW_WIDTH/4, DEFAULT_WINDOW_WIDTH/4); gtk_container_add(GTK_CONTAINER(window), vbox); gtk_widget_set_events(glarea, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); gtk_signal_connect(GTK_OBJECT(glarea), "realize", (GtkSignalFunc)init_gl, (gpointer)NULL); gtk_signal_connect(GTK_OBJECT(glarea), "expose_event", (GtkSignalFunc)redraw, (gpointer)NULL); gtk_signal_connect(GTK_OBJECT(glarea), "button_press_event", (GtkSignalFunc)mouse_button_down, (gpointer)NULL); gtk_signal_connect(GTK_OBJECT(glarea), "button_release_event", (GtkSignalFunc)mouse_button_up, (gpointer)NULL); gtk_signal_connect(GTK_OBJECT(glarea), "motion_notify_event", (GtkSignalFunc)mouse_motion, (gpointer)NULL); gtk_signal_connect(GTK_OBJECT(glarea), "configure_event", (GtkSignalFunc)reshape, (gpointer)NULL); build_menu(window,&menubar); gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox), combo_hbox, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(vbox), pane, TRUE, TRUE, 0); spinner = spinner_new(); info_button = gtk_button_new_with_label(_("Info")); gtk_signal_connect(GTK_OBJECT(info_button), "clicked", (GtkSignalFunc)info_button_callback, (gpointer)NULL); hbox = gtk_hbox_new(FALSE, 0); vbox2 = gtk_vbox_new(FALSE, 5); gtk_container_border_width(GTK_CONTAINER(vbox2), 5); #ifdef GTK_HAVE_FEATURES_1_1_5 dummyscrwin = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(dummyscrwin), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_widget_show(dummyscrwin); gtk_container_add(GTK_CONTAINER(dummyscrwin), clist); gtk_box_pack_start(GTK_BOX(hbox), dummyscrwin, TRUE, TRUE, 0); #else // GTK+ 1.0.x gtk_clist_set_policy (GTK_CLIST(clist), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_box_pack_start(GTK_BOX(hbox), clist, TRUE, TRUE, 0); #endif gtk_box_pack_start(GTK_BOX(hbox), vbox2, FALSE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox2), spinner, TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(vbox2), info_button, TRUE, TRUE, 0); notebook = gtk_notebook_new (); gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP); gtk_widget_show(notebook); /* FIXME! */ /* Now let's add all the pages. All of these are in separate files, and have local data. The traceroute page must be moved out of here. */ /* Traceroute part: */ label = gtk_label_new (_("Traceroute")); gtk_notebook_append_page (GTK_NOTEBOOK (notebook), hbox, label); gtk_widget_set_usize(GTK_WIDGET(notebook), -1, DEFAULT_WINDOW_HEIGHT/4); // for all other pages: // // widg = create_page(...) // label = gtk_label_new ("page name"); // gtk_notebook_append_page (GTK_NOTEBOOK (notebook), widg, label); gtk_paned_add1(GTK_PANED(pane), glarea); gtk_paned_add2(GTK_PANED(pane), notebook); /* This makes the glarea quadratic in the beginning. */ gtk_paned_set_position(GTK_PANED(pane), DEFAULT_WINDOW_WIDTH); gtk_widget_show(clist); gtk_widget_show(glarea); gtk_widget_show(pane); gtk_widget_show(info_button); gtk_widget_show(spinner); gtk_widget_show(vbox2); gtk_widget_show(vbox); gtk_widget_show(hbox); gtk_widget_show(menubar); gtk_widget_show(combo_label); gtk_widget_show(combo); gtk_widget_show(combo_hbox); gtk_widget_show(window); makeearth(); gtk_main(); return 0; /* ANSI C requires main to return int. */ }