/* Sysprof -- Sampling, systemwide CPU profiler * Copyright 2004, Red Hat, Inc. * Copyright 2004, 2005, 2006, 2007, Soeren Sandmann * * 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 #include #include #include #include #include #define _(a) a #define APPLICATION_NAME "Simple Image Viewer" #define PACKAGE_VERSION "0.0.1" #define GLADE_FILE "siv.glade" #define MIN_ZOOM -80 #define MAX_ZOOM 65 typedef struct Application Application; struct Application { GladeXML * xml; GdkPixbuf * original; GdkPixbuf * displayed; int zoom_level; gboolean first; }; #define GET_WIDGET(app,name) \ ((void *)glade_xml_get_widget(app->xml, name)) static void sorry (GtkWidget *parent_window, const gchar *format, ...) { va_list args; char *message; GtkWidget *dialog; va_start (args, format); g_vasprintf (&message, format, args); va_end (args); dialog = gtk_message_dialog_new (parent_window ? GTK_WINDOW (parent_window) : NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, message); g_free (message); gtk_window_set_title (GTK_WINDOW (dialog), APPLICATION_NAME " Warning"); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } static double get_scale (Application *app) { return pow (1.05, app->zoom_level); } static void compute_size (Application *app, int *w, int *h) { int d; double scale; if (!w) w = &d; if (!h) h = &d; scale = get_scale (app); *w = gdk_pixbuf_get_width (app->original) * scale; *h = gdk_pixbuf_get_height (app->original) * scale; } static gboolean on_expose (GtkWidget *drawing_area, GdkEventExpose *expose, Application *app) { int w, h; GdkPixbuf *tmp; GdkInterpType interp; gboolean has_alpha; GdkRectangle dest; int win_x, win_y; int window_width, window_height; GdkRectangle image; if (!app->original) return TRUE; if (!GTK_WIDGET_DRAWABLE (GET_WIDGET (app, "drawing_area"))) return TRUE; gdk_window_get_size (drawing_area->window, &window_width, &window_height); image.x = image.y = 0; compute_size (app, &image.width, &image.height); if (!gdk_rectangle_intersect (&(expose->area), &image, &dest)) return TRUE; win_x = dest.x; win_y = dest.y; if (image.width < window_width) win_x = (window_width - image.width) / 2; if (image.height < window_height) win_y = (window_height - image.height) / 2; /* We create a temporary pixbuf without alpha (since X is generally worthless) */ if (gtk_check_menu_item_get_active (GET_WIDGET (app, "menu_no"))) has_alpha = TRUE; else has_alpha = FALSE; tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, has_alpha, 8, dest.width, dest.height); if (gtk_check_menu_item_get_active (GET_WIDGET (app, "menu_no"))) { g_print ("no background\n"); /* FIXME: fill pixbuf with background color */ gdk_pixbuf_fill (tmp, 0x00000000); } else if (gtk_check_menu_item_get_active (GET_WIDGET (app, "menu_white"))) { g_print ("white background\n"); /* FIXME: fill with white */ gdk_pixbuf_fill (tmp, 0xffffffff); } else if (gtk_check_menu_item_get_active (GET_WIDGET (app, "menu_checkerboard"))) { g_print ("check\n"); /* FIXME: make checkerboard, or use composite_color */ } if (gtk_check_menu_item_get_active (GET_WIDGET (app, "menu_smooth_image"))) interp = GDK_INTERP_BILINEAR; else interp = GDK_INTERP_NEAREST; gdk_pixbuf_composite (app->original, tmp, 0, 0, dest.width, dest.height, - dest.x, - dest.y, get_scale (app), get_scale (app), interp, 0xFF); g_print ("asdf\n"); gdk_draw_pixbuf (drawing_area->window, NULL, tmp, 0, 0, win_x, win_y, dest.width, dest.height, GDK_RGB_DITHER_NONE, 0, 0); g_object_unref (tmp); return TRUE; } static void adjust_adjustment (GtkAdjustment *adj, int new_size) { double half_page; double value; if (!adj) return; g_print ("old value: %f\n", adj->value); g_print ("old size: %f\n", adj->upper); g_print ("new size: %d\n", new_size); half_page = adj->page_size / 2; value = new_size * ((adj->value + half_page) / adj->upper) - half_page; g_print ("new value: %f\n", value); gtk_adjustment_set_value (adj, value); } static void rebuild (Application *app) { gboolean can_zoom_out; gboolean can_zoom_in; if (app->zoom_level < MIN_ZOOM) app->zoom_level = MIN_ZOOM; if (app->zoom_level > MAX_ZOOM) app->zoom_level = MAX_ZOOM; if (!app->original) { can_zoom_out = FALSE; can_zoom_in = FALSE; } else { int w, h; GtkAdjustment *vadj, *hadj; double new_v; compute_size (app, &w, &h); can_zoom_in = app->zoom_level < MAX_ZOOM; can_zoom_out = app->zoom_level > MIN_ZOOM; if (app->zoom_level <= 0 && (w < 32 || h < 32)) can_zoom_out = FALSE; hadj = gtk_scrolled_window_get_hadjustment (GET_WIDGET (app, "scrolled_window")); vadj = gtk_scrolled_window_get_vadjustment (GET_WIDGET (app, "scrolled_window")); /* Hide the scrolled window so prevent an ugly flash while we adjust the position */ gtk_widget_hide (GET_WIDGET (app, "scrolled_window")); if (app->first) { gtk_adjustment_set_value (hadj, 0.0); gtk_adjustment_set_value (vadj, 0.0); app->first = FALSE; } else { adjust_adjustment (hadj, w); adjust_adjustment (vadj, h); } gtk_widget_show (GET_WIDGET (app, "scrolled_window")); gtk_widget_set_size_request (GET_WIDGET (app, "drawing_area"), w, h); } gtk_widget_set_sensitive (GET_WIDGET (app, "menu_zoom_out"), can_zoom_out); gtk_widget_set_sensitive (GET_WIDGET (app, "zoom_out"), can_zoom_out); gtk_widget_set_sensitive (GET_WIDGET (app, "menu_zoom_in"), can_zoom_in); gtk_widget_set_sensitive (GET_WIDGET (app, "zoom_in"), can_zoom_in); gtk_widget_queue_draw (GET_WIDGET (app, "drawing_area")); } static void on_zoom_in (GtkWidget *widget, Application *app) { app->zoom_level += 5; rebuild (app); }; static void on_zoom_out (GtkWidget *widget, Application *app) { app->zoom_level -= 5; rebuild (app); }; static void on_zoom_normal (GtkWidget *widget, Application *app) { app->zoom_level = 0; rebuild (app); }; static void on_rotate_left (GtkWidget *widget, Application *app) { g_print ("rotate_left\n"); }; static void on_rotate_right (GtkWidget *widget, Application *app) { g_print ("rotate_right\n"); }; static void on_various (GtkWidget *widget, Application *app) { rebuild (app); } static void on_scroll (GtkWidget *widget, GdkEventScroll *event, Application *app) { g_print ("scroll\n"); if (event->state & GDK_CONTROL_MASK) { if (event->direction == GDK_SCROLL_UP) app->zoom_level += 1; else if (event->direction == GDK_SCROLL_DOWN) app->zoom_level -= 1; else return; rebuild (app); } } static void connect_signals (Application *app) { int i; typedef struct { char widget[28]; char signal[16]; GCallback callback; } Info; static const Info connections[] = { { "main_window", "delete_event", G_CALLBACK (gtk_main_quit) }, { "zoom_in", "clicked", G_CALLBACK (on_zoom_in) }, { "zoom_out", "clicked", G_CALLBACK (on_zoom_out) }, { "zoom_normal", "clicked", G_CALLBACK (on_zoom_normal) }, { "rotate_left", "clicked", G_CALLBACK (on_rotate_left) }, { "rotate_right", "clicked", G_CALLBACK (on_rotate_right) }, { "menu_zoom_in", "activate", G_CALLBACK (on_zoom_in) }, { "menu_zoom_out", "activate", G_CALLBACK (on_zoom_out) }, { "menu_zoom_normal", "activate", G_CALLBACK (on_zoom_normal) }, { "menu_rotate_left", "activate", G_CALLBACK (on_rotate_left) }, { "menu_rotate_right", "activate", G_CALLBACK (on_rotate_right) }, { "menu_quit", "activate", G_CALLBACK (gtk_main_quit) }, { "menu_smooth_image", "toggled", G_CALLBACK (on_various) }, { "menu_white", "activate", G_CALLBACK (on_various) }, { "menu_no", "activate", G_CALLBACK (on_various) }, { "menu_checkerboard", "activate", G_CALLBACK (on_various) }, { "drawing_area", "expose_event", G_CALLBACK (on_expose) }, { "drawing_area", "scroll_event", G_CALLBACK (on_scroll) }, }; for (i = 0; i < G_N_ELEMENTS (connections); ++i) { const Info *info = &connections[i]; GtkWidget *widget = glade_xml_get_widget (app->xml, info->widget); if (widget) g_signal_connect (widget, info->signal, info->callback, app); else g_error ("Couldn't find widget %s\n", info->widget); } } static void set_icons (Application *app) { /* FIXME */ }; static gboolean build_gui (Application *app) { #if 0 set_shadows (); #endif if (!g_file_test (GLADE_FILE, G_FILE_TEST_EXISTS)) { sorry (NULL, APPLICATION_NAME " was not compiled or installed correctly.\n" "\n" "Running \"make install\" may solve this problem.\n"); return FALSE; } app->xml = glade_xml_new (GLADE_FILE, NULL, NULL); gtk_widget_add_events (GET_WIDGET (app, "drawing_area"), GDK_SCROLL_MASK); /* Connect signals */ connect_signals (app); app->zoom_level = 0; app->first = TRUE; rebuild (app); return TRUE; } static const char * process_options (int argc, char **argv) { int i; gboolean show_version = FALSE; const char *filename = NULL; for (i = 1; i < argc; ++i) { char *option = argv[i]; if (strcmp (option, "--version") == 0) { show_version = TRUE; } else if (!filename) { filename = argv[i]; } } if (show_version) { g_print ("%s %s\n", APPLICATION_NAME, PACKAGE_VERSION); exit (1); } return filename; } static void disable_g_slice (void) { /* Disable gslice, since it * * - confuses valgrind * - caches too much memory * - hides memory access bugs * - is not faster than malloc * * Note that g_slice_set_config() is broken in some versions of * GLib (and 'declared internal' according to Tim), so we use the * environment variable instead. */ if (!getenv ("G_SLICE")) putenv ("G_SLICE=always_malloc"); } typedef struct { const char *filename; Application *app; } FileOpenData; static gboolean load_file (gpointer data) { FileOpenData *open_data = data; GdkPixbuf *pixbuf; GError *err = NULL; pixbuf = gdk_pixbuf_new_from_file (open_data->filename, &err); if (!pixbuf) sorry (NULL, "Could not open %s: %s\n", open_data->filename, err->message); else { open_data->app->original = pixbuf; rebuild (open_data->app); } g_print ("orignal: %p\n", open_data->app->original); g_free (open_data); return FALSE; } int main (int argc, char **argv) { Application *app; const char *filename; disable_g_slice(); filename = process_options (argc, argv); gtk_init (&argc, &argv); app = g_new0 (Application, 1); if (!build_gui (app)) return -1; if (filename) { FileOpenData *file_open_data = g_new0 (FileOpenData, 1); file_open_data->filename = filename; file_open_data->app = app; /* This has to run at G_PRIORITY_LOW because of bug 350517 */ g_idle_add_full (G_PRIORITY_LOW, load_file, file_open_data, NULL); } gtk_main (); return 0; }