#include #include #include #include #include #include #include #include "siv.h" #include "sivviewport.h" #define MIN_ZOOM -80 #define MAX_ZOOM 80 struct SivWindow { GladeXML * xml; GtkWidget * drawing_area; char * filename; GdkPixbuf * original; int width; int height; int zoom_level; gboolean first; GtkAllocation allocation; App * app; int hadj; int vadj; gboolean dragging; int drag_x; int drag_y; double start_x; double start_y; guint32 last_motion_time; }; static void * get_widget (SivWindow *window, const char *name) { void *result; if (strcmp (name, "drawing_area") == 0) result = window->drawing_area; else result = glade_xml_get_widget (window->xml, name); if (!result) g_error ("Could not find widget %s\n", name); return result; } static GtkAdjustment * get_hadj (SivWindow *window) { return gtk_scrolled_window_get_hadjustment (get_widget (window, "scrolled_window")); } static GtkAdjustment * get_vadj (SivWindow *window) { return gtk_scrolled_window_get_vadjustment ( get_widget (window, "scrolled_window")); } static double get_scale (SivWindow *window) { return pow (1.05, window->zoom_level); } static void compute_size (SivWindow *window, int *w, int *h) { int d; double scale; if (!w) w = &d; if (!h) h = &d; scale = get_scale (window); *w = gdk_pixbuf_get_width (window->original) * scale; *h = gdk_pixbuf_get_height (window->original) * scale; } static void initialize_bg (GtkWidget *drawing_area, GdkRegion *region) { GdkWindowObject *private = (GdkWindowObject *)drawing_area->window; GdkGC *gc; if (gdk_region_empty (region)) return; gc = gdk_gc_new (drawing_area->window); gdk_gc_set_rgb_fg_color (gc, &(private->bg_color)); gdk_gc_set_clip_region (gc, region); gdk_draw_rectangle (drawing_area->window, gc, TRUE, 0, 0, -1, -1); g_object_unref (gc); } static void paint_rect (GtkWidget *drawing_area, GdkRectangle *area, SivWindow *window) { GdkPixbuf *tmp; GdkInterpType interp; GdkRectangle dest; int window_width, window_height; GdkRectangle image; guint32 color1, color2; GdkRegion *bg_reg, *image_reg; GdkPixmap *pixmap; GdkGC *gc; gdk_window_get_size (drawing_area->window, &window_width, &window_height); image.x = image.y = 0; compute_size (window, &image.width, &image.height); if (image.width < window_width) image.x = (window_width - image.width) / 2; if (image.height < window_height) image.y = (window_height - image.height) / 2; bg_reg = gdk_region_rectangle (area); image_reg = gdk_region_rectangle (&image); gdk_region_subtract (bg_reg, image_reg); initialize_bg (drawing_area, bg_reg); gdk_region_destroy (bg_reg); gdk_region_destroy (image_reg); if (!gdk_rectangle_intersect (area, &image, &dest)) return; tmp = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8, dest.width, dest.height); if (gtk_check_menu_item_get_active (get_widget (window, "menu_no"))) { GdkColor color = drawing_area->style->bg[GTK_STATE_NORMAL]; color1 = (color.red & 0xff00) << 8; color1 |= (color.green & 0xff00); color1 |= (color.blue & 0xff00) >> 8; color2 = color1; } else if (gtk_check_menu_item_get_active (get_widget (window, "menu_white"))) { color1 = 0x00ffffff; color2 = 0x00ffffff; } else if (gtk_check_menu_item_get_active (get_widget (window, "menu_black"))) { color1 = 0x00000000; color2 = 0x00000000; } else /* Checkerboard */ { color1 = 0x00808080; color2 = 0x00cccccc; } if (gtk_check_menu_item_get_active ( get_widget (window, "menu_smooth_image"))) { interp = GDK_INTERP_BILINEAR; } else { interp = GDK_INTERP_NEAREST; } gdk_pixbuf_composite_color (window->original, tmp, 0, 0, dest.width, dest.height, -(dest.x - image.x), -(dest.y - image.y), get_scale (window), get_scale (window), interp, 0xff, dest.x - image.x, dest.y - image.y, 16, color1, color2); pixmap = gdk_pixmap_new (drawing_area->window, dest.width, dest.height, -1); gdk_draw_pixbuf (pixmap, NULL, tmp, 0, 0, 0, 0, dest.width, dest.height, GDK_RGB_DITHER_NONE, 0, 0); gc = gdk_gc_new (drawing_area->window); gdk_draw_drawable (drawing_area->window, gc, pixmap, 0, 0, dest.x, dest.y, dest.width, dest.height); g_object_unref (gc); g_object_unref (pixmap); g_object_unref (tmp); } static gboolean on_expose (GtkWidget *drawing_area, GdkEventExpose *expose, SivWindow *window) { GdkRectangle *rects; int n_rects; int i; if (!GTK_WIDGET_DRAWABLE (get_widget (window, "drawing_area"))) return FALSE; if (!window->original) { initialize_bg (drawing_area, expose->region); return FALSE; } #if 0 paint_rect (drawing_area, &(expose->area), window); #endif gdk_region_get_rectangles (expose->region, &rects, &n_rects); #if 0 g_print ("expose\n"); #endif for (i = 0; i < n_rects; ++i) { #if 0 g_print (" %d %d %d %d\n", rects[i].x, rects[i].y, rects[i].width, rects[i].height); #endif paint_rect (drawing_area, &(rects[i]), window); } g_free (rects); return TRUE; } static void adjust_adjustment (GtkAdjustment *adj, int old_size, int new_size) { double half_page; double value; if (!adj) return; half_page = adj->page_size / 2; value = new_size * ((adj->value + half_page) / (double)old_size) - half_page; gtk_adjustment_set_value (adj, value); } static void set_sensitivity (SivWindow *window) { gboolean can_zoom_out = FALSE; gboolean can_zoom_in = FALSE; int i; /* List of widgets that are sensitive if and only if a file is loaded */ const char insensitive[][32] = { "menu_white", "menu_black", "menu_checkerboard", "menu_no", "menu_smooth_image", "menu_zoom_normal", "zoom_normal" }; for (i = 0; i < G_N_ELEMENTS (insensitive); ++i) { GtkWidget *widget = get_widget (window, insensitive[i]); if (window->original) gtk_widget_set_sensitive (widget, TRUE); else gtk_widget_set_sensitive (widget, FALSE); } if (window->original) { int w, h; compute_size (window, &w, &h); can_zoom_in = window->zoom_level < MAX_ZOOM; if (w > 32000 || h > 32000) { /* GTK+ doesn't support native windows larger than this. */ can_zoom_in = FALSE; } can_zoom_out = window->zoom_level > MIN_ZOOM; if (window->zoom_level <= 0 && (w < 16 || h < 16)) can_zoom_out = FALSE; } gtk_widget_set_sensitive (get_widget (window, "menu_zoom_out"), can_zoom_out); gtk_widget_set_sensitive (get_widget (window, "zoom_out"), can_zoom_out); gtk_widget_set_sensitive (get_widget (window, "menu_zoom_in"), can_zoom_in); gtk_widget_set_sensitive (get_widget (window, "zoom_in"), can_zoom_in); } static void set_title (SivWindow *window) { if (window->filename && window->original) { gchar *basename = g_path_get_basename (window->filename); gtk_window_set_title (get_widget (window, "main_window"), basename); g_free (basename); } else { gtk_window_set_title (get_widget (window, "main_window"), APPLICATION_NAME); } } static void set_status_bar (SivWindow *window) { GtkStatusbar *bar = get_widget (window, "status_bar"); gtk_statusbar_pop (bar, 0); if (window->original) { char *text; int width, height; width = gdk_pixbuf_get_width (window->original); height = gdk_pixbuf_get_height (window->original); #define TIMES "\303\227" text = g_strdup_printf (" %d "TIMES" %d %d%%", width, height, (int)(100 * get_scale (window) + 0.5)); gtk_statusbar_push (bar, 0, text); g_free (text); } } static void save_meta_data (SivWindow *window) { int x, y, width, height; const char *filename; BackgroundType bg; gboolean smooth; gboolean show_toolbar; gboolean show_status_bar; if (window->filename) filename = window->filename; else filename = "no file"; gtk_window_get_position (get_widget (window, "main_window"), &x, &y); gtk_window_get_size (get_widget (window, "main_window"), &width, &height); if (gtk_check_menu_item_get_active (get_widget (window, "menu_no"))) { bg = BG_NONE; } else if (gtk_check_menu_item_get_active (get_widget (window, "menu_white"))) { bg = BG_WHITE; } else if (gtk_check_menu_item_get_active (get_widget (window, "menu_checkerboard"))) { bg = BG_CHECKERBOARD; } else if (gtk_check_menu_item_get_active (get_widget (window, "menu_black"))) { bg = BG_BLACK; } else { bg = BG_NONE; } smooth = gtk_check_menu_item_get_active (get_widget (window, "menu_smooth_image")); show_toolbar = gtk_check_menu_item_get_active (get_widget (window, "menu_toolbar")); show_status_bar = gtk_check_menu_item_get_active (get_widget (window, "menu_status_bar")); app_set_meta_data (window->app, filename, x, y, width, height, smooth, bg, window->zoom_level, get_vadj (window)->value, get_hadj (window)->value, show_toolbar, show_status_bar); } static void rebuild (SivWindow *window) { if (window->zoom_level < MIN_ZOOM) window->zoom_level = MIN_ZOOM; if (window->zoom_level > MAX_ZOOM) window->zoom_level = MAX_ZOOM; if (window->original) { int w, h; GtkAdjustment *vadj, *hadj; GdkPixbuf *scaled; compute_size (window, &w, &h); gtk_widget_hide (get_widget (window, "scrolled_window")); hadj = get_hadj (window); vadj = get_vadj (window); if (window->first) { gtk_adjustment_set_value (hadj, 0.0); gtk_adjustment_set_value (vadj, 0.0); window->first = FALSE; } else { if (hadj->page_size < w) adjust_adjustment (hadj, window->width, w); if (vadj->page_size < h) adjust_adjustment (vadj, window->height, h); } gtk_widget_set_size_request (get_widget (window, "drawing_area"), w, h); /* The documentation for gtk_window_set_icon() says you shouldn't scale * the icon before passing it, but gdk_window_set_icon_list() ends up * issuing g_warnings if the icon is too big. */ if (gdk_pixbuf_get_width (window->original) >= 32 || gdk_pixbuf_get_height (window->original) >= 32) { /* However, scaling from huge to tiny is really slow with GdkPixbuf, * so in that case we do it ourselves in smaller steps. */ GdkPixbuf *tmp = g_object_ref (window->original); int width = gdk_pixbuf_get_width (tmp); int height = gdk_pixbuf_get_height (tmp); GdkPixbuf *t; while (width >= 1024 || height >= 1024) { width /= 2; height /= 2; t = gdk_pixbuf_scale_simple (tmp, width, height, GDK_INTERP_BILINEAR); g_object_unref (tmp); tmp = t; } t = gdk_pixbuf_scale_simple (tmp, 32, 32, GDK_INTERP_BILINEAR); g_object_unref (tmp); scaled = t; } else { scaled = g_object_ref (window->original); } gtk_window_set_icon (get_widget (window, "main_window"), scaled); g_object_unref (scaled); gtk_widget_show (get_widget (window, "scrolled_window")); window->width = w; window->height = h; } else { gtk_window_set_icon_name (get_widget (window, "main_window"), "siv"); } set_title (window); set_sensitivity (window); set_status_bar (window); if (gtk_check_menu_item_get_active (get_widget (window, "menu_toolbar"))) gtk_widget_show (get_widget (window, "toolbar")); else gtk_widget_hide (get_widget (window, "toolbar")); if (gtk_check_menu_item_get_active (get_widget (window, "menu_status_bar"))) gtk_widget_show (get_widget (window, "status_bar")); else gtk_widget_hide (get_widget (window, "status_bar")); gtk_widget_queue_draw (get_widget (window, "drawing_area")); } static void zoom (SivWindow *window, int delta) { GtkWidget *z; if (delta == 0) return; if (delta < 0) z = get_widget (window, "zoom_out"); else z = get_widget (window, "zoom_in"); if (GTK_WIDGET_SENSITIVE (z)) { window->zoom_level += delta; rebuild (window); } } static void on_zoom_in (GtkWidget *widget, SivWindow *window) { zoom (window, 5); }; static void on_zoom_out (GtkWidget *widget, SivWindow *window) { zoom (window, -5); }; static void on_zoom_normal (GtkWidget *widget, SivWindow *window) { zoom (window, -window->zoom_level); }; static gboolean on_scroll (GtkWidget *widget, GdkEventScroll *event, SivWindow *window) { if (event->state & GDK_CONTROL_MASK) { if (event->direction == GDK_SCROLL_UP) { zoom (window, 1); return TRUE; } else if (event->direction == GDK_SCROLL_DOWN) { zoom (window, -1); return TRUE; } } return FALSE; } static gboolean on_mouse_press (GtkWidget *widget, GdkEventButton *button, SivWindow *window) { if (button->button == 1) { GdkCursor *cursor = gdk_cursor_new (GDK_FLEUR); if (gdk_pointer_grab (widget->window, FALSE, GDK_BUTTON1_MOTION_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK, NULL, cursor, button->time) == GDK_GRAB_SUCCESS) { window->dragging = TRUE; window->drag_x = button->x_root; window->drag_y = button->y_root; window->start_x = get_hadj (window)->value; window->start_y = get_vadj (window)->value; } gdk_cursor_unref (cursor); } return TRUE; } static gboolean on_mouse_release (GtkWidget *widget, GdkEventButton *button, SivWindow *window) { if (button->button == 1 && window->dragging) { gdk_pointer_ungrab (button->time); window->dragging = FALSE; } return TRUE; } typedef struct { Window window; guint32 last_motion_time; gboolean stop_compressing; } EventScannerData; static Bool get_motion_time (Display *dpy, XEvent *xevent, XPointer arg) { EventScannerData *esd = (EventScannerData *)arg; if (esd->stop_compressing) return FALSE; if (esd->window != xevent->xany.window) { esd->stop_compressing = TRUE; return FALSE; } if (xevent->type == MotionNotify) { esd->last_motion_time = xevent->xmotion.time; return FALSE; } if (xevent->type == EnterNotify || xevent->type == LeaveNotify) { esd->last_motion_time = xevent->xcrossing.time; return FALSE; } return FALSE; } static void set_adjustment (GtkAdjustment *adj, int dv) { dv = CLAMP (dv, adj->lower, adj->upper - adj->page_size); gtk_adjustment_set_value (adj, dv); } static gboolean use_this_motion_notify (GdkWindow *window, guint32 event_time, guint32 *motion_notify_time) { if (*motion_notify_time) { if (event_time == *motion_notify_time) { *motion_notify_time = 0; return TRUE; } else { /* There are more motion events already queued up */ return FALSE; } } else { Display *dpy = gdk_x11_get_default_xdisplay(); EventScannerData esd = { GDK_WINDOW_XWINDOW (window), 0, FALSE }; XEvent dummy; XCheckIfEvent (dpy, &dummy, get_motion_time, (XPointer)&esd); if (esd.last_motion_time == 0) { return TRUE; } else { *motion_notify_time = esd.last_motion_time; return FALSE; } } } static void handle_motion (SivWindow *window, guint32 time, int x, int y) { GtkWidget *widget = get_widget (window, "drawing_area"); if (!use_this_motion_notify ( widget->window, time, &(window->last_motion_time))) { return; } if (window->dragging) { GtkAdjustment *vadj = get_vadj (window); GtkAdjustment *hadj = get_hadj (window); int dx = window->drag_x - x; int dy = window->drag_y - y; #if 0 gdk_window_freeze_updates (widget->window); #endif set_adjustment (hadj, window->start_x + dx); set_adjustment (vadj, window->start_y + dy); #if 0 gdk_window_thaw_updates (widget->window); #endif } } static void on_motion (GtkWidget *widget, GdkEventMotion *motion, SivWindow *window) { handle_motion (window, motion->time, motion->x_root, motion->y_root); } static void on_crossing (GtkWidget *widget, GdkEventCrossing *crossing, SivWindow *window) { handle_motion (window, crossing->time, crossing->x_root, crossing->y_root); } static void on_various (GtkWidget *widget, SivWindow *window) { rebuild (window); } static void on_size_allocate (GtkWidget *widget, GtkAllocation *allocation, gpointer data) { SivWindow *window = data; if (window->allocation.width == 0 || window->allocation.height == 0) { gtk_widget_queue_draw (widget); } else if (GTK_WIDGET_REALIZED (widget)) { GdkRectangle old; GdkRectangle new; GdkRegion *region; int dx, dy; if (!window->original) return; compute_size (window, &old.width, &old.height); old.x = (window->allocation.width - old.width) / 2; old.y = (window->allocation.height - old.height) / 2; new = old; new.x = (allocation->width - old.width) / 2; new.y = (allocation->height - old.height) / 2; if (old.width < allocation->width && old.width < window->allocation.width) { dx = new.x - old.x; } else { dx = 0; if (old.width < allocation->width || old.width < window->allocation.width) gtk_widget_queue_draw (widget); } if (old.height < allocation->height && old.height < window->allocation.height) { dy = new.y - old.y; } else { dy = 0; if (old.height < allocation->height || old.height < window->allocation.height) gtk_widget_queue_draw (widget); } region = gdk_region_rectangle (&old); gdk_window_move_region (widget->window, region, dx, dy); gdk_region_destroy (region); } window->allocation = *allocation; } static void on_close_event (GtkWidget *widget, GdkEvent *event, gpointer data) { SivWindow *window = data; window_free (window); } static void on_close (GtkWidget *widget, gpointer data) { SivWindow *window = data; window_free (window); } static void set_busy (GtkWidget *widget, gboolean busy) { GdkCursor *cursor; GdkWindow *window; if (busy) cursor = gdk_cursor_new (GDK_WATCH); else cursor = NULL; if (GTK_IS_TEXT_VIEW (widget)) window = gtk_text_view_get_window (GTK_TEXT_VIEW (widget), GTK_TEXT_WINDOW_TEXT); else window = widget->window; gdk_window_set_cursor (window, cursor); if (cursor) gdk_cursor_unref (cursor); gdk_display_flush (gdk_display_get_default ()); } static void add_image_filters (GtkFileChooser *chooser) { GtkFileFilter *filter; filter = gtk_file_filter_new (); gtk_file_filter_add_pixbuf_formats (filter); gtk_file_filter_set_name (filter, "Images"); gtk_file_chooser_add_filter (chooser, filter); filter = gtk_file_filter_new (); gtk_file_filter_set_name (filter, "All Files"); gtk_file_filter_add_pattern (filter, "*"); gtk_file_chooser_add_filter (chooser, filter); } static void on_open (GtkWidget *widget, gpointer data) { SivWindow *window = data; gchar *filename = NULL; GtkFileChooser *chooser; set_busy (get_widget (window, "main_window"), TRUE); chooser = GTK_FILE_CHOOSER (app_get_open_chooser (window->app)); gtk_window_set_transient_for (GTK_WINDOW (chooser), get_widget (window, "main_window")); gtk_window_set_modal (GTK_WINDOW (chooser), TRUE); add_image_filters (chooser); set_busy (get_widget (window, "main_window"), FALSE); retry: if (gtk_dialog_run (GTK_DIALOG (chooser)) == GTK_RESPONSE_ACCEPT) { GError *err = NULL; SivWindow *target; gboolean success; char *tmp; if (window->original) target = window_new (window->app); else target = window; filename = gtk_file_chooser_get_filename (chooser); /* This broke in GTK+ 2.13 */ if (!g_path_is_absolute (filename)) { char *folder; folder = gtk_file_chooser_get_current_folder (chooser); tmp = g_build_filename (folder, filename, NULL); g_free (folder); g_free (filename); filename = tmp; } tmp = nul_canonicalize_filename (filename); g_free (filename); filename = tmp; set_busy (GTK_WIDGET (chooser), TRUE); success = window_load_file (target, filename, &err); set_busy (GTK_WIDGET (chooser), FALSE); if (success) { if (target != window) window_show (target, NULL); } else { app_show_could_not_open ( get_widget (window, "main_window"), 1, &filename); g_error_free (err); if (window != target) window_free (target); } g_free (filename); if (!success) goto retry; } gtk_widget_hide (GTK_WIDGET (chooser)); } static void on_about (GtkWidget *widget, gpointer data) { #define OSLASH "\303\270" SivWindow *window = data; gtk_show_about_dialog (get_widget (window, "main_window"), "program-name", APPLICATION_NAME, "copyright", "Copyright 2008, 2009, 2010, S"OSLASH"ren Sandmann", "logo-icon-name", "siv", "version", PACKAGE_VERSION, NULL); } static void connect_signals (SivWindow *window) { int i; typedef struct { char widget[32]; char signal[32]; GCallback callback; } Info; static const Info connections[] = { { "main_window", "delete_event", G_CALLBACK (on_close_event) }, { "zoom_in", "clicked", G_CALLBACK (on_zoom_in) }, { "zoom_out", "clicked", G_CALLBACK (on_zoom_out) }, { "zoom_normal", "clicked", G_CALLBACK (on_zoom_normal) }, { "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_close", "activate", G_CALLBACK (on_close) }, { "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) }, { "menu_toolbar", "activate", G_CALLBACK (on_various) }, { "menu_status_bar", "activate", G_CALLBACK (on_various) }, { "drawing_area", "expose_event", G_CALLBACK (on_expose) }, { "drawing_area", "scroll_event", G_CALLBACK (on_scroll) }, { "drawing_area", "size_allocate", G_CALLBACK (on_size_allocate) }, { "drawing_area", "button_press_event", G_CALLBACK (on_mouse_press) }, { "drawing_area", "button_release_event", G_CALLBACK (on_mouse_release) }, { "drawing_area", "motion_notify_event", G_CALLBACK (on_motion) }, { "drawing_area", "leave_notify_event", G_CALLBACK (on_crossing) }, { "drawing_area", "enter_notify_event", G_CALLBACK (on_crossing) }, { "menu_open", "activate", G_CALLBACK (on_open) }, { "menu_about", "activate", G_CALLBACK (on_about) }, }; for (i = 0; i < G_N_ELEMENTS (connections); ++i) { const Info *info = &connections[i]; GtkWidget *widget = get_widget (window, info->widget); if (widget) g_signal_connect (widget, info->signal, info->callback, window); else g_error ("Couldn't find widget %s\n", info->widget); } } static void set_defaults (SivWindow *window) { GtkWidget *widget = get_widget (window, "main_window"); GdkScreen *screen = gtk_widget_get_screen (widget); GdkRectangle monitor; int monitor_num; int width, height; monitor_num = gdk_screen_get_monitor_at_window (screen, widget->window); gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor); if (window->original) { width = gdk_pixbuf_get_width (window->original) + 48; height = gdk_pixbuf_get_height (window->original) + 96; } else { if (monitor.height < monitor.width) { height = (monitor.height * 3) / 4; width = (3 * height) / 4; } else { width = (monitor.width) / 3; height = (width * 4) / 3; } } if (width > monitor.width) width = monitor.width - 32; if (height > monitor.height) height = monitor.height - 32; if (GTK_WIDGET_REALIZED (get_widget (window, "main_window"))) gtk_window_resize (get_widget (window, "main_window"), width, height); else gtk_window_set_default_size (get_widget (window, "main_window"), width, height); gtk_window_move (get_widget (window, "main_window"), (monitor.width - width) / 2, (monitor.height - height) / 2); gtk_check_menu_item_set_active (get_widget (window, "menu_smooth_image"), TRUE); gtk_check_menu_item_set_active (get_widget (window, "menu_no"), TRUE); gtk_check_menu_item_set_active (get_widget (window, "menu_toolbar"), TRUE); gtk_check_menu_item_set_active (get_widget (window, "menu_status_bar"), FALSE); } static void apply_meta_data (SivWindow *window, const char *filename) { GtkCheckMenuItem *item; MetaData data; if (app_get_meta_data (window->app, filename, &data)) { gtk_window_move (get_widget (window, "main_window"), data.window_x, data.window_y); gtk_window_resize (get_widget (window, "main_window"), data.window_width, data.window_height); switch (data.background) { default: case BG_NONE: item = get_widget (window, "menu_no"); break; case BG_CHECKERBOARD: item = get_widget (window, "menu_checkerboard"); break; case BG_WHITE: item = get_widget (window, "menu_white"); break; case BG_BLACK: item = get_widget (window, "menu_black"); break; } gtk_check_menu_item_set_active (item, TRUE); gtk_check_menu_item_set_active (get_widget (window, "menu_smooth_image"), data.smooth_image); gtk_check_menu_item_set_active (get_widget (window, "menu_toolbar"), data.show_toolbar); gtk_check_menu_item_set_active (get_widget (window, "menu_status_bar"), data.show_status_bar); window->zoom_level = data.zoom_level; /* Adjustments have to be set after the window is shown */ window->hadj = data.hadj; window->vadj = data.vadj; } else { set_defaults (window); } } static gboolean load_file (SivWindow *window, const char *filename, GError **err) { GdkPixbuf *pixbuf; pixbuf = gdk_pixbuf_new_from_file (filename, err); if (pixbuf) { if (window->original) g_object_unref (window->original); window->original = pixbuf; /* Make a copy of filename because it may be the same * as window->filename which we free below. */ filename = g_strdup (filename); if (window->filename) g_free (window->filename); window->filename = (char *)filename; apply_meta_data (window, filename); rebuild (window); return TRUE; } else { return FALSE; } } void window_reload (SivWindow *window) { if (window->filename) { save_meta_data (window); load_file (window, window->filename, NULL); } } gboolean window_load_file (SivWindow *window, const char *filename, GError **err) { return load_file (window, filename, err); } SivWindow * window_new (App *app) { SivWindow *window = g_new0 (SivWindow, 1); GtkWidget *scrolled_window, *viewport; window->xml = glade_xml_new (GLADE_FILE, NULL, NULL); window->app = app; scrolled_window = get_widget (window, "scrolled_window"); viewport = siv_viewport_new (NULL, NULL); window->drawing_area = gtk_drawing_area_new (); gtk_container_add (GTK_CONTAINER (scrolled_window), viewport); gtk_container_add (GTK_CONTAINER (viewport), window->drawing_area); gtk_widget_show (viewport); gtk_widget_show (window->drawing_area); gtk_widget_add_events (get_widget (window, "drawing_area"), GDK_SCROLL_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK); gtk_widget_set_double_buffered (get_widget (window, "drawing_area"), FALSE); gtk_widget_set_redraw_on_allocate (get_widget (window, "drawing_area"), FALSE); gtk_widget_realize (get_widget (window, "main_window")); /* Connect signals */ connect_signals (window); window->zoom_level = 0; window->first = TRUE; apply_meta_data (window, "no file"); rebuild (window); app_register_window (app, window); return window; } void window_free (SivWindow *window) { save_meta_data (window); app_unregister_window (window->app, window); gtk_widget_destroy (get_widget (window, "main_window")); g_object_unref (window->xml); if (window->original) g_object_unref (window->original); if (window->filename) g_free (window->filename); g_free (window); } /* Cutted and pasted from GTK+ */ static guint32 extract_time_from_startup_id (const gchar* startup_id) { gchar *timestr = g_strrstr (startup_id, "_TIME"); guint32 retval = GDK_CURRENT_TIME; if (timestr) { gchar *end; guint32 timestamp; /* Skip past the "_TIME" part */ timestr += 5; errno = 0; timestamp = strtoul (timestr, &end, 0); if (end != timestr && errno == 0) retval = timestamp; } return retval; } void window_present (SivWindow *window, const char *startup_id) { GtkWindow *w = get_widget (window, "main_window"); guint32 time = GDK_CURRENT_TIME; if (startup_id) { gtk_window_set_startup_id (w, startup_id); time = extract_time_from_startup_id (startup_id); } if (time == GDK_CURRENT_TIME) time = gtk_get_current_event_time(); gtk_window_present_with_time (w, time); } void window_show (SivWindow *window, const char *startup_id) { GtkAdjustment *vadj, *hadj; gtk_widget_show (get_widget (window, "main_window")); gtk_window_set_focus (get_widget (window, "main_window"), NULL); /* We have to do this after the window is shown */ hadj = gtk_scrolled_window_get_hadjustment ( get_widget (window, "scrolled_window")); vadj = gtk_scrolled_window_get_vadjustment ( get_widget (window, "scrolled_window")); gtk_adjustment_set_value (vadj, window->vadj); gtk_adjustment_set_value (hadj, window->hadj); window_present (window, startup_id); } gboolean window_matches_file (SivWindow *window, const char *filename) { return window->filename && strcmp (filename, window->filename) == 0; } gboolean window_is_blank (SivWindow *window) { return !window->filename; }