/* tgraph.c - Created by Giampiero Caprino This file is part of Train Director Train Director 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, or (at your option) any later version. Train Director 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 Train Director; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #if !defined(__unix__) #include #endif #include "trsim.h" #define STATION_WIDTH 100 #define KM_WIDTH 50 #define HEADER_HEIGHT 20 #define MAXWIDTH (4 * 60 * 24 + STATION_WIDTH + KM_WIDTH) extern Track *layout; extern int is_windows; static Track *stations[60]; static int nstations; static GdkPixmap *pixmap; static GdkPixmap *top_pixmap; static GtkWidget *top_drawing_area; typedef struct { GtkWidget *drawing_area; GdkGC *gc; int hmult, vmult; } grid; grid *tgraph_grid; int highkm; static guchar colortable[12][3] = { { 0, 0, 0 }, { 255, 255, 255 }, { 0, 255, 0 }, { 255, 255, 0 }, { 255, 0, 0 }, { 255, 128, 0 }, { 255, 128, 128 }, { 128, 128, 128 }, { 192, 192, 192 }, { 64, 64, 64 }, { 0, 0, 128 }, { 0, 255, 255 } }; void tgr_clear_field(void) { grid *g; g = tgraph_grid; if(!g->gc) g->gc = gdk_gc_new(g->drawing_area->window); gdk_rgb_gc_set_foreground(g->gc, (colortable[fieldcolors[COL_GRAPHBG]][0] << 16) | (colortable[fieldcolors[COL_GRAPHBG]][1] << 8) | (colortable[fieldcolors[COL_GRAPHBG]][2])); gdk_draw_rectangle(pixmap, g->gc, TRUE, 0, 0, MAXWIDTH, 1000); /*gdk_draw_rectangle(top_pixmap, g->gc, TRUE, 0, 0, MAXWIDTH, 15);*/ } void tgr_draw_all_pixmap() { gdk_draw_pixmap(tgraph_grid->drawing_area->window, tgraph_grid->drawing_area->style->fg_gc[ GTK_WIDGET_STATE(tgraph_grid->drawing_area)], pixmap, 0, 0, 0, 0, MAXWIDTH, 1000); /*gdk_draw_pixmap(top_drawing_area->window, tgraph_grid->drawing_area->style->fg_gc[ GTK_WIDGET_STATE(tgraph_grid->drawing_area)], top_pixmap, 0, 0, 0, 0, MAXWIDTH, 15);*/ } static void draw_time_grid(int y) { int h, m; int x; char buff[20]; GtkStyle *style; x = STATION_WIDTH + KM_WIDTH; style = tgraph_grid->drawing_area->style; gdk_rgb_gc_set_foreground(tgraph_grid->gc, (colortable[7][0] << 16) | (colortable[7][1] << 8) | (colortable[7][2])); for(h = 0; h < 24; ++h) for(m = 0; m < 60; ++m) { if((m % 10)) { gdk_draw_line(pixmap, style->black_gc, x + h * 240 + m * 4, y - 2, x + h * 240 + m * 4, y + 2); } else { gdk_draw_line(pixmap, m ? tgraph_grid->gc : style->black_gc, x + h * 240 + m * 4, 20, x + h * 240 + m * 4, 1000); } } for(h = 0; h < 24; ++h) { sprintf(buff, "%2d:00", h); gdk_draw_string(pixmap, style->font, style->black_gc, x + h * 240, 10, buff); } } static int km_to_y(int km) { int y; y = HEADER_HEIGHT + (double)km / (double)highkm * 960; return y; } static int islinkedtext(Track *t) { if(t->elinkx && t->elinky) return 1; if(t->wlinkx && t->wlinky) return 1; return 0; } static void draw_stations(void) { Track *t; int y; char *p; GdkRectangle update_rect; GtkStyle *style; char buff[64]; nstations = 0; highkm = 0; for(t = layout; t; t = t->next) { if(t->type == TEXT) { if(!islinkedtext(t)) continue; if(t->km > highkm) highkm = t->km; continue; } if(!t->isstation || !t->station || !t->km) continue; if(t->km > highkm) highkm = t->km; } style = tgraph_grid->drawing_area->style; for(t = layout; t; t = t->next) { if(t->type == TEXT) { if(!t->km || !islinkedtext(t)) continue; } else if(!t->isstation || !t->station || !t->km) continue; stations[nstations++] = t; y = km_to_y(t->km); draw_time_grid(y); sprintf(buff, "%3d.%d %s", t->km / 1000, (t->km / 100) % 10, t->station); if((p = strchr(buff, '@'))) *p = 0; update_rect.x = 0; update_rect.y = y; update_rect.width = gdk_string_width(style->font, buff); update_rect.height = gdk_string_height(style->font, buff); gdk_draw_string(pixmap, style->font, style->black_gc, 0, y + update_rect.height / 2, buff); } if(!nstations) { gdk_draw_string(pixmap, style->font, style->black_gc, 40, 40, L("Sorry, this feature is not available on this scenario.")); gdk_draw_string(pixmap, style->font, style->black_gc, 40, 60, L("No station has distance information.")); } } static int graphstation(char *st) { int i; for(i = 0; i < nstations; ++i) if(sameStation(st, stations[i]->station)) return i; return -1; } static void graph_xy(long km, long tim, int *x, int *y) { *x = tim / 60 * 4 + STATION_WIDTH + KM_WIDTH; *y = km_to_y(km); } void time_to_time(int x, int y, int nx, int ny) { GdkGC *gc; if(nx < x) /* ignore if arrival is next day */ return; gc = tgraph_grid->gc /*drawing_area->style->black_gc*/; if(ny < y) { /* going from bottom of graph to top */ gdk_draw_line(pixmap, gc, x, y - 5, nx, ny + 5); gdk_draw_line(pixmap, gc, x, y, x, y - 5); gdk_draw_line(pixmap, gc, nx, ny + 5, nx, ny); } else { /* going from top of graph to bottom */ gdk_draw_line(pixmap, gc, x, y + 5, nx, ny - 5); gdk_draw_line(pixmap, gc, x, y, x, y + 5); gdk_draw_line(pixmap, gc, nx, ny - 5, nx, ny); } } static int samestation(char *st, char *arrdep) { char buff[256]; int i; for(i = 0; *arrdep && *arrdep != ' '; buff[i++] = *arrdep++); buff[i] = 0; return(sameStation(st, buff)); } static void draw_trains(void) { GdkGC *gc; Train *t; TrainStop *ts; Track *trk; int indx; int x, y; int nx, ny; for(t = schedule; t; t = t->next) { gc = tgraph_grid->gc; gdk_rgb_gc_set_foreground(gc, (colortable[fieldcolors[COL_TRAIN1+t->type]][0] << 16) | (colortable[fieldcolors[COL_TRAIN1+t->type]][1] << 8) | (colortable[fieldcolors[COL_TRAIN1+t->type]][2])); x = y = -1; for(trk = layout; trk; trk = trk->next) { if(trk->type == TRACK && trk->isstation && samestation(trk->station, t->entrance)) break; if(trk->type == TEXT && islinkedtext(trk) && trk->km > 0 && samestation(trk->station, t->entrance)) break; } if(trk && (indx = graphstation(trk->station)) >= 0) graph_xy(stations[indx]->km, t->timein, &x, &y); for(ts = t->stops; ts; ts = ts->next) { indx = graphstation(ts->station); if(indx < 0) continue; if(x == -1) { graph_xy(stations[indx]->km, ts->departure, &x, &y); continue; } graph_xy(stations[indx]->km, ts->arrival, &nx, &ny); time_to_time(x, y, nx, ny); graph_xy(stations[indx]->km, ts->departure, &x, &y); } if(x != -1) { for(trk = layout; trk; trk = trk->next) { if(trk->type == TRACK && trk->isstation && samestation(trk->station, t->exit)) break; if(trk->type == TEXT && islinkedtext(trk) && trk->km > 0 && samestation(trk->station, t->exit)) break; } if(!trk) continue; indx = graphstation(trk->station); if(indx < 0) continue; graph_xy(stations[indx]->km, t->timeout, &nx, &ny); time_to_time(x, y, nx, ny); } } } void tgr_repaint_all(void) { if(!tgraph_grid->gc) tgraph_grid->gc = gdk_gc_new(tgraph_grid->drawing_area->window); tgr_clear_field(); draw_stations(); draw_trains(); tgr_draw_all_pixmap(); } /* Create a new backing pixmap of the appropriate size */ static gint tgr_configure_event( GtkWidget *widget, GdkEventConfigure *event ) { if(pixmap) return 0; /* gdk_pixmap_unref(pixmap); */ pixmap = gdk_pixmap_new(widget->window, MAXWIDTH /* widget->allocation.width */, 1000 /* widget->allocation.height */, -1); /*top_pixmap = gdk_pixmap_new(top_drawing_area->window, MAXWIDTH, 15, -1);*/ tgr_clear_field(); return TRUE; } /* Redraw the screen from the backing pixmap */ gint tgr_expose_event( GtkWidget *widget, GdkEventExpose *event ) { gdk_draw_pixmap(widget->window, widget->style->fg_gc[GTK_WIDGET_STATE(widget)], pixmap, event->area.x, event->area.y, event->area.x, event->area.y, event->area.width, event->area.height); return FALSE; } gint scroll_changed( GtkAdjustment *a, GtkWidget *w) { long l; printf("H = %6g\r", a->lower); fflush(stdout); return 0; } /* Create the drawing area */ int create_tgraph_draw(GtkWidget *window) { GtkAdjustment *adj; grid *g; g = (grid *)calloc(sizeof(grid), 1); g->hmult = 1; g->vmult = 1; tgraph_grid = g; g->drawing_area = gtk_drawing_area_new(); gtk_drawing_area_size(GTK_DRAWING_AREA(g->drawing_area), MAXWIDTH, 1000); /* pack the drawing area into the scrolled window */ gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(window), g->drawing_area); #if 0 adj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(window)); gtk_signal_connect(GTK_OBJECT(adj), "value_changed", (GtkSignalFunc) scroll_changed, window); #endif gtk_widget_show(g->drawing_area); /* Signals used to handle backing pixmap */ gtk_signal_connect(GTK_OBJECT(g->drawing_area), "expose_event", (GtkSignalFunc) tgr_expose_event, NULL); gtk_signal_connect(GTK_OBJECT(g->drawing_area),"configure_event", (GtkSignalFunc) tgr_configure_event, NULL); /* Event signals */ #if 0 gtk_signal_connect(GTK_OBJECT(g->drawing_area), "button_press_event", (GtkSignalFunc) tgr_button_press_event, NULL); #endif gtk_widget_set_events(g->drawing_area, GDK_EXPOSURE_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK); return 0; } static int destroy(GtkWidget *w) { if(tgraph_grid->drawing_area) gtk_widget_destroy(tgraph_grid->drawing_area); gtk_widget_destroy(w); free(tgraph_grid); tgraph_grid = 0; return 1; } int create_tgraph(void) { GtkWidget *window; GtkWidget *scrolled_window; GtkWidget *vbox; char buffer[32]; int i, j; window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_widget_set_usize(GTK_WIDGET(window), 800, 200); gtk_window_set_title(GTK_WINDOW(window), L("Train Schedule Graph")); gtk_signal_connect(GTK_OBJECT(window), "destroy", GTK_SIGNAL_FUNC(gtk_widget_destroy), NULL); vbox = gtk_vbox_new(FALSE, 5); gtk_container_set_border_width(GTK_CONTAINER(vbox), 5); gtk_container_add(GTK_CONTAINER(window), vbox); gtk_widget_show(vbox); #if 0 top_drawing_area = gtk_drawing_area_new(); gtk_drawing_area_size(GTK_DRAWING_AREA(top_drawing_area), MAXWIDTH, 15); gtk_box_pack_end(GTK_BOX(vbox), top_drawing_area, FALSE, FALSE, 0); gtk_widget_show(top_drawing_area); #endif /* create a new scrolled window. */ scrolled_window = gtk_scrolled_window_new(NULL, NULL); gtk_container_set_border_width(GTK_CONTAINER(scrolled_window), 10); /* the policy is one of GTK_POLICY AUTOMATIC, or GTK_POLICY_ALWAYS. * GTK_POLICY_AUTOMATIC will automatically decide whether you need * scrollbars, whereas GTK_POLICY_ALWAYS will always leave the scrollbars * there. The first one is the horizontal scrollbar, the second, * the vertical. */ gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); gtk_box_pack_start(GTK_BOX(vbox), scrolled_window, TRUE, TRUE, 0); gtk_widget_show(scrolled_window); create_tgraph_draw(scrolled_window); gtk_widget_show(window); tgr_repaint_all(); return(0); }