/* gtviewer.c * GeoTIFF Viewer. Simple tool for displaying geotiffs. * * Copyright (C) 2006-2012 Jonathan duSaint * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include #include #include #include #include "config.h" #include "icon.h" /* macros for creating DD MM SS.FF from DD.FFF */ #define degrees(x) (floor (fabs (x))) #define minutes(x) (floor (fmod (fabs (x) * 60.0, 60.0))) #define seconds(x) (fmod((fabs(x) * 60.0), 1.0) * 60.0) /* macros dealing with scaling factors and zooming */ /* scale width or height (x) using the zoom level (f) */ #define scale(x,f) ((int)((x) + (double)(f) * (x) / 10.0)) /* max and min zoom levels - the app slows waaaay down when MAX_ZOOM is > 5 */ #define MAX_ZOOM 5 #define MIN_ZOOM -9 /* how to display the coordinates on the status bar */ enum coordinate_display { PIXELS, MAPUNITS, GEOCOORDS }; /* what kind of zoom to do */ enum zoom_action { ZOOM_IN, ZOOM_OUT, ZOOM_FULL }; /* which direction to scale the offset in when zooming */ enum offset_direction { X_OFFSET, Y_OFFSET }; /* the main window - actual type is GtkWindow */ GtkWidget *main_window; /* the image area - actual type is GtkDrawingArea */ GtkWidget *image; /* the status bar - actual type is GtkStatusBar */ GtkWidget *status; /* status context to use for coordinate display */ gint status_context; /* the last directory visited - used when opening a file */ gchar *last_directory = NULL; /* the original pixbuf that is created when a file is opened */ GdkPixbuf *pb_original = NULL; /* the pixbuf being displayed - this may be a reference to the original */ GdkPixbuf *pb = NULL; /* mouse starting x - updated when the button goes down */ gdouble mouse_start_x = -1; /* mouse starting y - updated when the button goes down */ gdouble mouse_start_y = -1; /* whether or not the mouse button is down */ gboolean mouse_is_down = FALSE; /* x offset into the pixbuf */ gdouble x_offset = 0; /* y offset into the pixbuf */ gdouble y_offset = 0; /* the current zoom factor - this can range from MIN_ZOOM to MAX_ZOOM */ gint zoom_factor = 0; /* TIFF file handle - used by libtiff */ TIFF *tiff = NULL; /* GeoTIFF file handle - used by libgeotiff */ GTIF *geotiff = NULL; /* GeoTIFF projection definition */ GTIFDefn gtifdefn; /* which kind of coordinates to display in the status bar */ enum coordinate_display current_coordinates = GEOCOORDS; /* open_geotiff * This function does all the hard work of actually opening a geotiff, * extracting its information (including projection), and creating * the pixbuf. * * Input: * filename - full path of the file to open */ void open_geotiff (char *filename) { gchar *title; GdkPixbuf *pbnew; GError *err = NULL; /* load the image into a pixbuf */ pbnew = gdk_pixbuf_new_from_file (filename, &err); /* throw an error if the file didn't load */ if (pbnew == NULL) { GtkWidget *d; d = gtk_message_dialog_new (GTK_WINDOW (main_window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "Error loading file '%s': %s", filename, err->message); gtk_dialog_run (GTK_DIALOG (d)); gtk_widget_destroy (d); return; } /* clean up if needed */ if (geotiff != NULL) GTIFFree (geotiff); if (tiff != NULL) XTIFFClose (tiff); /* open the geotiff, get its info, and read the data */ tiff = XTIFFOpen (filename, "r"); if (tiff == NULL) { GtkWidget *d; g_object_unref (pbnew); d = gtk_message_dialog_new (GTK_WINDOW (main_window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "Mystery error in libtiff opening file '%s'", filename); gtk_dialog_run (GTK_DIALOG (d)); gtk_widget_destroy (d); return; } geotiff = GTIFNew (tiff); if (geotiff == NULL) { GtkWidget *d; g_object_unref (pbnew); XTIFFClose (tiff); d = gtk_message_dialog_new (GTK_WINDOW (main_window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "Mystery error in libgeotiff with file '%s'", filename); gtk_dialog_run (GTK_DIALOG (d)); gtk_widget_destroy (d); return; } /* get the geotiff projection info */ GTIFGetDefn (geotiff, >ifdefn); /* destroy the old pixbufs */ if (pb != NULL) gdk_pixbuf_unref (pb); if (pb_original != NULL) gdk_pixbuf_unref (pb_original); /* load succeeded, save the new pixbuf */ pb_original = pbnew; pb = g_object_ref (pb_original); /* change the window title to reflect the filename */ title = g_strjoin (" - ", PACKAGE_NAME, g_path_get_basename (filename), NULL); gtk_window_set_title (GTK_WINDOW (main_window), title); g_free (title); /* reset viewport and zoom */ x_offset = 0; y_offset = 0; zoom_factor = 0; } /* check_offset * Verify that a given X or Y offset to the current viewport is within range, * and fix it if it isn't. * * Input: * off - the raw offset * d - which direction the offset is in (x or y) * * Output: * the corrected offset. */ int check_offset (int off, enum offset_direction d) { int max_o = 0;; switch (d) { case X_OFFSET: max_o = gdk_pixbuf_get_width (pb) - image->allocation.width; break; case Y_OFFSET: max_o = gdk_pixbuf_get_height (pb) - image->allocation.height; } if (off < 0) off = 0; if (off > max_o) off = max_o; return off; } /* zoom_image * Rescale the pixbuf in, out, or to the original size. * * Input: * zoom - what kind of zoom to do */ void zoom_image (enum zoom_action zoom) { gint zf = 0; /* internal zoom factor */ gint wc, hc; /* current pixbuf width and height */ gint wo, ho; /* original pixbuf width and height */ /* adjust the zoom factor */ switch (zoom) { case ZOOM_IN: zf = zoom_factor + 1; break; case ZOOM_OUT: zf = zoom_factor - 1; break; case ZOOM_FULL: zf = 0; break; } /* return without doing anything if it will go out of bounds */ if (zf > MAX_ZOOM) return; if (zf < MIN_ZOOM) return; /* put up the watch cursor */ gdk_window_set_cursor (main_window->window, gdk_cursor_new (GDK_WATCH)); gdk_window_process_updates (main_window->window, TRUE); /* set the global zoom factor */ zoom_factor = zf; /* get dimensions of pixbufs */ wc = gdk_pixbuf_get_width (pb); hc = gdk_pixbuf_get_height (pb); wo = gdk_pixbuf_get_width (pb_original); ho = gdk_pixbuf_get_height (pb_original); /* the original pb is no longer needed */ g_object_unref (pb); /* if zoom factor is 0 (no zoom), just ref the original, else scale it */ if (zf == 0) pb = g_object_ref (pb_original); else pb = gdk_pixbuf_scale_simple (pb_original, scale (wo, zf), scale (ho,zf), GDK_INTERP_NEAREST); /* scale offsets */ x_offset = check_offset (((x_offset + (gdouble)image->allocation.width / 2) * (gdouble)gdk_pixbuf_get_width (pb) / (gdouble)wc) - (gdouble)image->allocation.width / 2, X_OFFSET); y_offset = check_offset (((y_offset + (gdouble)image->allocation.height / 2) * (gdouble)gdk_pixbuf_get_height (pb) / (gdouble)hc) - (gdouble)image->allocation.height / 2, Y_OFFSET); /* reset cursor */ gdk_window_set_cursor (main_window->window, gdk_cursor_new (GDK_LEFT_PTR)); /* redraw */ gtk_widget_queue_draw_area (image, 0, 0, image->allocation.width, image->allocation.height); } /* get_window_size * Get good values for the main window width and height. The window will be * 3/4 as wide and 3/4 as high as the screen. * * Input: * w - handle to main window * * Output: * ww - ideal window width * wh - ideal window height */ void get_window_size (GtkWindow *w, int *ww, int *wh) { *ww = gdk_screen_get_width (gtk_window_get_screen (w)) * 3 / 4; *wh = gdk_screen_get_height (gtk_window_get_screen (w)) * 3 / 4; } /** menu callbacks **/ /* menu_open_file * Callback for File->Open. Prompts the user to select a file and calls * open_geotiff to do the work. */ void menu_open_file (GtkAction *action, gpointer user_data) { GtkWidget *fdialog; GtkFileFilter *filter; /* prompt the user for a file */ fdialog = gtk_file_chooser_dialog_new ("Open GeoTIFF", GTK_WINDOW (main_window), GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL); /* filter by MIME type */ filter = gtk_file_filter_new (); gtk_file_filter_add_mime_type (filter, "image/tiff"); gtk_file_filter_add_pattern (filter, "*.tif"); gtk_file_filter_add_pattern (filter, "*.tiff"); gtk_file_filter_set_name (filter, "GeoTIFF Files"); gtk_file_chooser_add_filter (GTK_FILE_CHOOSER (fdialog), filter); /* change to the last directory, if there is one */ if (last_directory != NULL) { gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (fdialog), last_directory); g_free (last_directory); } if (gtk_dialog_run (GTK_DIALOG (fdialog)) == GTK_RESPONSE_ACCEPT) { char *filename; /* save the current directory */ last_directory = gtk_file_chooser_get_current_folder (GTK_FILE_CHOOSER (fdialog)); /* open the file */ filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (fdialog)); open_geotiff (filename); g_free (filename); /* signal that the screen needs to be drawn */ gtk_widget_queue_draw_area (image, 0, 0, image->allocation.width, image->allocation.height); } gtk_widget_destroy (fdialog); } /* menu_view_coordinates * Callback for View->Display Pixels, View->Display Image Units, and * View->Display Geocoordinates. Toggles the status bar display between * different types of units. */ void menu_view_coordinates (GtkRadioAction *action, GtkRadioAction *c, gpointer d) { switch (gtk_radio_action_get_current_value (action)) { case 0: current_coordinates = GEOCOORDS; break; case 1: current_coordinates = PIXELS; break; case 2: current_coordinates = MAPUNITS; break; } /* clean up the status bar (current text is now invalid) */ gtk_statusbar_pop (GTK_STATUSBAR (status), status_context); gtk_statusbar_push (GTK_STATUSBAR (status), status_context, " "); } /* menu_help_contents * Callback for Help->Contents. Display the help file in the help viewer. */ void menu_help_contents (GtkAction *action, gpointer user_data) { int ww, wh; /* window width/height */ gchar *help_text; GError *err; GtkWidget *help, *view, *scroll; GtkTextBuffer *buf; /* load help file (gtviewer.txt) */ if (!g_file_get_contents (HELP_FILE, &help_text, NULL, &err)) { GtkWidget *d; d = gtk_message_dialog_new (GTK_WINDOW (main_window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "Error loading help text: %s", err->message); gtk_dialog_run (GTK_DIALOG (d)); gtk_widget_destroy (d); return; } /* create help window */ help = gtk_dialog_new_with_buttons ("Help for GeoTIFF Viewer", GTK_WINDOW (main_window), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_STOCK_CLOSE, GTK_RESPONSE_CLOSE, NULL); g_signal_connect_swapped (help, "response", G_CALLBACK (gtk_widget_destroy), help); get_window_size (GTK_WINDOW (main_window), &ww, &wh); gtk_window_set_default_size (GTK_WINDOW (help), ww / 2, wh / 2); view = gtk_text_view_new (); buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)); gtk_text_buffer_set_text (buf, help_text, -1); gtk_text_view_set_editable (GTK_TEXT_VIEW (view), FALSE); scroll = gtk_scrolled_window_new (NULL, NULL); gtk_container_add (GTK_CONTAINER (scroll), view); gtk_box_pack_start (GTK_BOX (GTK_DIALOG (help)->vbox), scroll, TRUE, TRUE,0); gtk_widget_show_all (help); g_free (help_text); } /* menu_help_about * Callback for Help->About. Display some application information. */ void menu_help_about (GtkAction *action, gpointer user_data) { char *authors[] = { "Jonathan duSaint ", NULL }; gtk_show_about_dialog (GTK_WINDOW (main_window), "authors", authors, "comments", PACKAGE_NAME, "version", PACKAGE_VERSION, "website", "http://www.rockgeeks.net/jon/", NULL); } /* exit_gtviewer * Callback to exit the program. Called as a callback when the window is * closed, and called by menu_exit. */ gboolean exit_gtviewer (GtkWidget *widget, GdkEvent *thisevent, gpointer data) { /* clean up */ if (pb != NULL) g_object_unref (pb); if (pb_original != NULL) g_object_unref (pb_original); if (geotiff != NULL) GTIFFree (geotiff); if (tiff != NULL) XTIFFClose (tiff); if (last_directory != NULL) g_free (last_directory); gtk_main_quit (); return TRUE; } /* menu_exit * Callback for File->Exit. */ void menu_exit (GtkAction *action, gpointer user_data) { exit_gtviewer (NULL, NULL, user_data); } /** signal callback **/ /* expose_image * Callback handler for expose_event. Draw the contents of the pixbuf * into the image widget. */ gboolean expose_image (GtkWidget *widget, GdkEventExpose *event, gpointer data) { gint width = event->area.width; /* exposed width */ gint height = event->area.height; /* exposed height */ gint x_center = 0; /* x center of image widget */ gint y_center = 0; /* y center of image widget */ gint pb_width; /* width of current pixbuf */ gint pb_height; /* height of current pixbuf */ if (geotiff == NULL) return TRUE; pb_width = gdk_pixbuf_get_width (pb); pb_height = gdk_pixbuf_get_height (pb); /* handle the special case of when the viewport is larger than the pixbuf */ if (width > pb_width) { x_offset = 0; x_center = (width - pb_width) / 2; width = pb_width; } if (height > pb_height) { y_offset = 0; y_center = (height - pb_height) / 2; height = pb_height; } /* draw the area */ gdk_draw_pixbuf (image->window, image->style->fg_gc[GTK_STATE_NORMAL], pb, event->area.x + x_offset, event->area.y + y_offset, event->area.x + x_center, event->area.y + y_center, width, height, GDK_RGB_DITHER_NORMAL, 0, 0); return TRUE; } /* configure_image * Callback handler for configure_event. Make sure that data is available * when the window is resized and adjust viewport accordingly. */ gboolean configure_image (GtkWidget *widget, GdkEventConfigure *event, gpointer data) { if (geotiff == NULL) return TRUE; /* shrink offset if the window is resized past the right/bottom edge */ if ((int)x_offset + event->width > gdk_pixbuf_get_width (pb)) x_offset -= (int)x_offset + event->width - gdk_pixbuf_get_width (pb); if ((int)y_offset + event->height > gdk_pixbuf_get_height (pb)) y_offset -= (int)y_offset + event->height - gdk_pixbuf_get_height (pb); /* redraw */ gtk_widget_queue_draw_area (image, 0, 0, event->width, event->height); return TRUE; } /* mouse_click * Callback handler for button_press_event and button_release_event. For * a normal press or release, adjust the mouse coordinates and set the mouse * down flag accordingly. Reset the zoom on double click. */ gboolean mouse_click (GtkWidget *widget, GdkEventButton *event, gpointer data) { if (geotiff == NULL) return TRUE; if (event->type == GDK_BUTTON_PRESS) { /* normal mouse down */ mouse_start_x = event->x; mouse_start_y = event->y; mouse_is_down = TRUE; } else if (event->type == GDK_BUTTON_RELEASE) { /* normal mouse up */ mouse_start_x = -1; mouse_start_y = -1; mouse_is_down = FALSE; } else if (event->type == GDK_2BUTTON_PRESS) { /* double click */ zoom_image (ZOOM_FULL); } return TRUE; } /* scroll_image * Callback handler for scroll_event. Moving the scroll wheel up zooms in, * moving it down zooms out. */ gboolean scroll_image (GtkWidget *widget, GdkEventScroll *event, gpointer data) { if (geotiff == NULL) return TRUE; switch (event->direction) { case GDK_SCROLL_UP: case GDK_SCROLL_LEFT: zoom_image (ZOOM_IN); break; case GDK_SCROLL_DOWN: case GDK_SCROLL_RIGHT: zoom_image (ZOOM_OUT); break; } return TRUE; } void show_pixel (gdouble x, gdouble y) { guchar *p; p = (gdk_pixbuf_get_pixels (pb) + (int)y * gdk_pixbuf_get_rowstride (pb) + (int)x * gdk_pixbuf_get_n_channels (pb)); //printf("%lf,%lf: %x,%x,%x,%x\n", x, y, p[0], p[1], p[2], p[3]); // rgba } /* mouse_moving * Callback handler for motion_notify_event. If a button is down, the map * is scrolled. If not, the display coordinates are updated. */ gboolean mouse_moving (GtkWidget *widget, GdkEventMotion *event, gpointer data) { gdouble x, y; gchar message[48]; if (geotiff == NULL) return TRUE; /* check if pointer is down */ if (mouse_is_down == TRUE) { /* scroll entire image */ gdouble x_off = x_offset - (event->x - mouse_start_x); gdouble y_off = y_offset - (event->y - mouse_start_y); /* adjust mouse start for (possible) next event */ mouse_start_x = event->x; mouse_start_y = event->y; /* reset viewport */ x_offset = check_offset (x_off, X_OFFSET); y_offset = check_offset (y_off, Y_OFFSET); /* redraw */ gtk_widget_queue_draw_area (image, 0, 0, image->allocation.width, image->allocation.height); } else { /* update status area with coordinates */ x = event->x + x_offset; y = event->y + y_offset; switch (current_coordinates) { case PIXELS: g_snprintf (message, 48, "%d Y, %d X", (gint)y, (gint)x); break; case MAPUNITS: GTIFImageToPCS (geotiff, &x, &y); g_snprintf (message, 48, "%lf N, %lf E", y, x); break; case GEOCOORDS: GTIFImageToPCS (geotiff, &x, &y); GTIFProj4ToLatLong (>ifdefn, 1, &x, &y); g_snprintf (message, 48, "%2d %2d'%6.3f\"%c, %3d %2d'%6.3f\"%c", (gint)degrees (y), (gint)minutes (y), seconds (y), (y < 0) ? 'S' : 'N', (gint)degrees (x), (gint)minutes (x), seconds (x), (x < 0) ? 'W' : 'E'); break; } gtk_statusbar_pop (GTK_STATUSBAR (status), status_context); gtk_statusbar_push (GTK_STATUSBAR (status), status_context, message); /* update widget with color under mouse */ show_pixel(event->x, event->y); } return TRUE; } /* init_ui * Build the main window and all of its widgets. * * Output: * the main window */ GtkWidget * init_ui (void) { int ww, wh; /* window width/height */ GtkWidget *window, *main_vbox, *menubar; /* menu items */ gchar menu_items[] = "" " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " " ""; GtkActionEntry menu_entries[] = { { "FileMenu", NULL, "_File" }, { "ViewMenu", NULL, "_View" }, { "HelpMenu", NULL, "_Help" }, { "Open", GTK_STOCK_OPEN, "_Open", "O", "Open a GeoTIFF", G_CALLBACK (menu_open_file) }, { "Exit", GTK_STOCK_QUIT, "E_xit", "Q", "Exit the program", G_CALLBACK (menu_exit) }, { "Contents", GTK_STOCK_HELP, "_Contents", "F1", "Display application help", G_CALLBACK (menu_help_contents) }, { "About", GTK_STOCK_ABOUT, "_About", NULL, "Display information about this application", G_CALLBACK (menu_help_about) } }; GtkRadioActionEntry radio_entries[] = { { "Geo", NULL, "Display _Geocoordinates", NULL, "Show X,Y as geographical coordinates", 0 }, { "Pixel", NULL, "Display _Pixels", NULL, "Show X,Y as pixels", 1 }, { "TIFF", NULL, "Display _Image Units", NULL, "Show X,Y as native image coordinates", 2 } }; GtkActionGroup *action_group; GtkUIManager *ui_manager; GtkAccelGroup *accel_group; GError *error; /* create the toplevel window */ window = gtk_window_new (GTK_WINDOW_TOPLEVEL); get_window_size (GTK_WINDOW (window), &ww, &wh); gtk_window_set_title (GTK_WINDOW (window), PACKAGE_NAME); gtk_window_set_default_size (GTK_WINDOW (window), ww, wh); gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER); g_signal_connect (G_OBJECT (window), "delete_event", G_CALLBACK (exit_gtviewer), NULL); main_vbox = gtk_vbox_new (FALSE, 1); gtk_container_add (GTK_CONTAINER (window), main_vbox); /* build the menus */ action_group = gtk_action_group_new ("MenuActions"); gtk_action_group_add_actions (action_group, menu_entries, G_N_ELEMENTS (menu_entries), window); gtk_action_group_add_radio_actions (action_group, radio_entries, G_N_ELEMENTS (radio_entries), 0, G_CALLBACK (menu_view_coordinates), window); ui_manager = gtk_ui_manager_new (); gtk_ui_manager_insert_action_group (ui_manager, action_group, 0); accel_group = gtk_ui_manager_get_accel_group (ui_manager); gtk_window_add_accel_group (GTK_WINDOW (window), accel_group); error = NULL; if (!gtk_ui_manager_add_ui_from_string (ui_manager, menu_items, -1, &error)) { g_message ("building menus failed: %s", error->message); g_error_free (error); exit (EXIT_FAILURE); } menubar = gtk_ui_manager_get_widget (ui_manager, "/MainMenu"); gtk_box_pack_start (GTK_BOX (main_vbox), menubar, FALSE, TRUE, 0); /* create the display area */ image = gtk_drawing_area_new (); gtk_widget_add_events (image, (GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_EXPOSURE_MASK | GDK_STRUCTURE_MASK | GDK_SCROLL_MASK)); g_signal_connect (G_OBJECT (image), "expose_event", G_CALLBACK (expose_image), NULL); g_signal_connect (G_OBJECT (image), "configure_event", G_CALLBACK (configure_image), NULL); g_signal_connect (G_OBJECT (image), "motion_notify_event", G_CALLBACK (mouse_moving), NULL); g_signal_connect (G_OBJECT (image), "scroll_event", G_CALLBACK (scroll_image), NULL); g_signal_connect (G_OBJECT (image), "button_press_event", G_CALLBACK (mouse_click), NULL); g_signal_connect (G_OBJECT (image), "button_release_event", G_CALLBACK (mouse_click), NULL); gtk_box_pack_start (GTK_BOX (main_vbox), image, TRUE, TRUE, 0); /* create the status area */ status = gtk_statusbar_new (); status_context = gtk_statusbar_get_context_id (GTK_STATUSBAR (status), "mouse"); gtk_statusbar_push (GTK_STATUSBAR (status), status_context, " "); gtk_box_pack_end (GTK_BOX (main_vbox), status, FALSE, TRUE, 0); /* show everything and return */ gtk_widget_show_all (window); return window; } /* version * Command line invoked --version output. */ void version (void) { g_print ("%s %s\n" "Copyright (C) 2006-2012 Jonathan duSaint\n" "This program is free software; you may redistribute it under the\n" "terms of the GNU General Public License. This program comes\n" "with absolutely no warranty.\n", PACKAGE_NAME, PACKAGE_VERSION); } /* help * Command line invoked --help output. */ void help (void) { g_print ("Display a GeoTIFF file.\n" "\n" "Usage: gtviewer [options]\n" "Either of the following command line options may be given:\n" " -h, --help Display this message\n" " -v, --version Display this programs version number\n" "\n" "Report bugs, problems, and suggestions to .\n"); } /* main * GeoTIFF viewer entry point. */ int main (int argc, char *argv[]) { GdkPixbuf *icon; GError *err = NULL; gtk_init (&argc, &argv); /* check for options */ if (argc > 1 && (!strcmp (argv[1], "-h") || !strcmp (argv[1], "--help"))) { help (); return 0; } else if (argc > 1 && (!strcmp (argv[1], "-v") || !strcmp (argv[1], "--version"))) { version (); return 0; } main_window = init_ui (); g_set_application_name (PACKAGE_NAME); /* set the icon */ icon = gdk_pixbuf_from_pixdata (>viewer_icon, TRUE, &err); if (icon != NULL) { gtk_window_set_icon (GTK_WINDOW (main_window), icon); g_object_unref (icon); } gtk_main (); return 0; }