URI: 
       croptool.c - croptool - Image cropping tool
  HTML git clone git://lumidify.org/croptool.git (fast, but not encrypted)
  HTML git clone https://lumidify.org/croptool.git (encrypted, but very slow)
  HTML git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/croptool.git (over tor)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
       croptool.c (17849B)
       ---
            1 /*
            2  * Copyright (c) 2020 lumidify <nobody[at]lumidify.org>
            3  *
            4  * Permission to use, copy, modify, and distribute this software for any
            5  * purpose with or without fee is hereby granted, provided that the above
            6  * copyright notice and this permission notice appear in all copies.
            7  *
            8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
            9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
           10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
           11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
           12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
           13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
           14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
           15  */
           16 
           17 #include <stdio.h>
           18 #include <limits.h>
           19 #include <stdlib.h>
           20 #include <math.h>
           21 #include <gtk/gtk.h>
           22 #include <cairo/cairo.h>
           23 #include <gdk/gdkkeysyms.h>
           24 
           25 /* The number of pixels to check on each side when checking
           26  * if a corner or edge of the selection box was clicked 
           27  * (in order to change the size of the box) */
           28 static const int COLLISION_PADDING = 10;
           29 /* The color of the selection box */
           30 static const char *SELECTION_COLOR1 = "#000";
           31 /* The second selection color - when tab is pressed */
           32 static const char *SELECTION_COLOR2 = "#fff";
           33 
           34 /* Change this if you want a different output format. */
           35 static void
           36 print_cmd(const char *filename, int x, int y, int w, int h) {
           37         printf("mogrify -crop %dx%d+%d+%d '%s'\n", w, h, x, y, filename);
           38 }
           39 
           40 struct Rect {
           41         int x0;
           42         int y0;
           43         int x1;
           44         int y1;
           45 };
           46 
           47 struct Point {
           48         int x;
           49         int y;
           50 };
           51 
           52 struct Selection {
           53         struct Rect rect;
           54         int orig_w;
           55         int orig_h;
           56         int scaled_w;
           57         int scaled_h;
           58 };
           59 
           60 struct State {
           61         struct Selection **selections;
           62         char **filenames;
           63         int cur_selection;
           64         int num_files;
           65         int window_w;
           66         int window_h;
           67         GdkPixbuf *cur_pixbuf;
           68         struct Point move_handle;
           69         gboolean moving;
           70         gboolean resizing;
           71         gboolean lock_x;
           72         gboolean lock_y;
           73         GdkColor col1;
           74         GdkColor col2;
           75         int cur_col;
           76 };
           77 
           78 static void swap(int *a, int *b);
           79 static void sort_coordinates(int *x0, int *y0, int *x1, int *y1);
           80 static int collide_point(int x, int y, int x_point, int y_point);
           81 static int collide_line(int x, int y, int x0, int y0, int x1, int y1);
           82 static int collide_rect(int x, int y, struct Rect rect);
           83 static void redraw(GtkWidget *area, struct State *state);
           84 static void destroy(GtkWidget *widget, gpointer data);
           85 static gboolean draw_expose(GtkWidget *area, GdkEvent *event, gpointer data);
           86 static gboolean button_press(GtkWidget *area, GdkEventButton *event, gpointer data);
           87 static gboolean button_release(GtkWidget *area, GdkEventButton *event, gpointer data);
           88 static gboolean drag_motion(GtkWidget *area, GdkEventMotion *event, gpointer data);
           89 static gboolean key_press(GtkWidget *area, GdkEventKey *event, gpointer data);
           90 static gboolean configure_event(GtkWidget *area, GdkEvent *event, gpointer data);
           91 static void change_picture(GtkWidget *area, GdkPixbuf *new_pixbuf, int new_selection,
           92     int orig_w, int orig_h, struct State *state, gboolean copy_box);
           93 static void next_picture(GtkWidget *area, struct State *state, gboolean copy_box);
           94 static void last_picture(GtkWidget *area, struct State *state);
           95 static GdkPixbuf *load_pixbuf(char *filename, int w, int h, int *actual_w, int *actual_h);
           96 static void print_selection(struct Selection *sel, const char *filename);
           97 static void clear_selection(GtkWidget *area, struct State *state);
           98 static void resize_manual(GtkWidget *area, struct State *state);
           99 static void switch_color(GtkWidget *area, struct State *state);
          100 
          101 int main(int argc, char *argv[]) {
          102         GtkWidget *window;
          103         gtk_init(&argc, &argv);
          104 
          105         argc--;
          106         argv++;
          107         if (argc < 1) {
          108                 fprintf(stderr, "No file given\n");
          109                 exit(1);
          110         }
          111 
          112         struct State *state = malloc(sizeof(struct State));
          113         state->cur_pixbuf = NULL;
          114         state->selections = malloc(argc * sizeof(struct Selection *));
          115         state->num_files = argc;
          116         state->filenames = argv;
          117         state->cur_selection = -1;
          118         state->moving = FALSE;
          119         state->resizing = FALSE;
          120         state->lock_x = FALSE;
          121         state->lock_y = FALSE;
          122         state->window_w = 0;
          123         state->window_h = 0;
          124         state->cur_col = 1;
          125         for (int i = 0; i < argc; i++) {
          126                 state->selections[i] = NULL;
          127         }
          128 
          129         window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
          130         gtk_window_set_title(GTK_WINDOW(window), "croptool");
          131         gtk_window_set_default_size(GTK_WINDOW(window), 500, 500);
          132         g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(destroy), NULL);
          133 
          134         GtkWidget *area = gtk_drawing_area_new();
          135         GTK_WIDGET_SET_FLAGS(area, GTK_CAN_FOCUS);
          136         gtk_widget_add_events(area,
          137             GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
          138             GDK_BUTTON_MOTION_MASK | GDK_KEY_PRESS_MASK |
          139             GDK_POINTER_MOTION_HINT_MASK | GDK_POINTER_MOTION_MASK);
          140         gtk_container_add(GTK_CONTAINER(window), area);
          141 
          142         g_signal_connect(area, "expose-event", G_CALLBACK(draw_expose), state);
          143         g_signal_connect(area, "button-press-event", G_CALLBACK(button_press), state);
          144         g_signal_connect(area, "button-release-event", G_CALLBACK(button_release), state);
          145         g_signal_connect(area, "motion-notify-event", G_CALLBACK(drag_motion), state);
          146         g_signal_connect(window, "configure-event", G_CALLBACK(configure_event), state);
          147         g_signal_connect(window, "key-press-event", G_CALLBACK(key_press), state);
          148 
          149         gtk_widget_show_all(window);
          150 
          151         GdkColormap *cmap = gdk_drawable_get_colormap(area->window);
          152         gdk_colormap_alloc_color(cmap, &state->col1, FALSE, TRUE);
          153         gdk_color_parse(SELECTION_COLOR1, &state->col1);
          154         gdk_colormap_alloc_color(cmap, &state->col2, FALSE, TRUE);
          155         gdk_color_parse(SELECTION_COLOR2, &state->col2);
          156         g_object_unref(cmap);
          157 
          158         gtk_main();
          159 
          160         for (int i = 0; i < argc; i++) {
          161                 if (state->selections[i]) {
          162                         print_selection(state->selections[i], argv[i]);
          163                         free(state->selections[i]);
          164                 }
          165         }
          166         if (state->cur_pixbuf)
          167                 g_object_unref(G_OBJECT(state->cur_pixbuf));
          168         free(state->selections);
          169         free(state);
          170 
          171         return 0;
          172 }
          173 
          174 static void
          175 swap(int *a, int *b) {
          176         int tmp = *a;
          177         *a = *b;
          178         *b = tmp;
          179 }
          180 
          181 static void
          182 sort_coordinates(int *x0, int *y0, int *x1, int *y1) {
          183         if (*x0 > *x1)
          184                 swap(x0, x1);
          185         if(*y0 > *y1)
          186                 swap(y0, y1);
          187 }
          188 
          189 static void
          190 print_selection(struct Selection *sel, const char *filename) {
          191         /* The box was never actually used */
          192         if (sel->rect.x0 == -200)
          193                 return;
          194         double scale = (double)sel->orig_w / sel->scaled_w;
          195         int x0 = sel->rect.x0, y0 = sel->rect.y0;
          196         int x1 = sel->rect.x1, y1 = sel->rect.y1;
          197         sort_coordinates(&x0, &y0, &x1, &y1);
          198         x0 = round(x0 * scale);
          199         y0 = round(y0 * scale);
          200         x1 = round(x1 * scale);
          201         y1 = round(y1 * scale);
          202         /* The box is completely outside of the picture. */
          203         if (x0 >= sel->orig_w || y0 >= sel->orig_h)
          204                 return;
          205         /* Cut the bounding box if it goes past the end of the picture. */
          206         x0 = x0 < 0 ? 0 : x0;
          207         y0 = y0 < 0 ? 0 : y0;
          208         x1 = x1 > sel->orig_w ? sel->orig_w : x1;
          209         y1 = y1 > sel->orig_h ? sel->orig_h : y1;
          210         print_cmd(filename, x0, y0, x1 - x0, y1 - y0);
          211 }
          212 
          213 static GdkPixbuf *
          214 load_pixbuf(char *filename, int w, int h, int *actual_w, int *actual_h) {
          215         (void)gdk_pixbuf_get_file_info(filename, actual_w, actual_h);
          216         /* *actual_w and *actual_h can be garbage if the file doesn't exist */
          217         w = w < *actual_w || *actual_w < 0 ? w : *actual_w;
          218         h = h < *actual_h || *actual_h < 0 ? h : *actual_h;
          219         GError *err = NULL;
          220         GdkPixbuf *pix = gdk_pixbuf_new_from_file_at_size(filename, w, h, &err);
          221         if (err) {
          222                 fprintf(stderr, "%s\n", err->message);
          223                 g_error_free(err);
          224                 return NULL;
          225         }
          226         return pix;
          227 }
          228 
          229 static void
          230 destroy(GtkWidget *widget, gpointer data) {
          231         gtk_main_quit();
          232 }
          233 
          234 static int
          235 collide_point(int x, int y, int x_point, int y_point) {
          236         return (abs(x - x_point) <= COLLISION_PADDING) &&
          237                 (abs(y - y_point) <= COLLISION_PADDING);
          238 }
          239 
          240 static int
          241 collide_line(int x, int y, int x0, int y0, int x1, int y1) {
          242         sort_coordinates(&x0, &y0, &x1, &y1);
          243         /* this expects a valid line */
          244         if (x0 == x1) {
          245                 return (abs(x - x0) <= COLLISION_PADDING) &&
          246                         (y0 <= y) && (y <= y1);
          247         } else {
          248                 return (abs(y - y0) <= COLLISION_PADDING) &&
          249                         (x0 <= x) && (x <= x1);
          250         }
          251 }
          252 
          253 static int
          254 collide_rect(int x, int y, struct Rect rect) {
          255         int x0 = rect.x0, x1 = rect.x1;
          256         int y0 = rect.y0, y1 = rect.y1;
          257         sort_coordinates(&x0, &y0, &x1, &y1);
          258         return (x0 <= x) && (x <= x1) && (y0 <= y) && (y <= y1);
          259 }
          260 
          261 static gboolean
          262 button_press(GtkWidget *area, GdkEventButton *event, gpointer data) {
          263         struct State *state = (struct State *)data;
          264         if (state->cur_selection < 0 || !state->selections[state->cur_selection])
          265                 return FALSE;
          266         struct Rect *rect = &state->selections[state->cur_selection]->rect;
          267         gint x = event->x;
          268         gint y = event->y;
          269         int x0 = rect->x0, x1 = rect->x1;
          270         int y0 = rect->y0, y1 = rect->y1;
          271         if (collide_point(x, y, x0, y0)) {
          272                 rect->x0 = x1;
          273                 rect->y0 = y1;
          274                 rect->x1 = x;
          275                 rect->y1 = y;
          276         } else if (collide_point(x, y, x1, y1)) {
          277                 rect->x1 = x;
          278                 rect->y1 = y;
          279         } else if (collide_point(x, y, x0, y1)) {
          280                 rect->x0 = rect->x1;
          281                 rect->x1 = x;
          282                 rect->y1 = y;
          283         } else if (collide_point(x, y, x1, y0)) {
          284                 rect->y0 = y1;
          285                 rect->x1 = x;
          286                 rect->y1 = y;
          287         } else if (collide_line(x, y, x0, y0, x1, y0)) {
          288                 state->lock_y = TRUE;
          289                 swap(&rect->x0, &rect->x1);
          290                 rect->y0 = rect->y1;
          291                 rect->y1 = y;
          292         } else if (collide_line(x, y, x0, y0, x0, y1)) {
          293                 state->lock_x = TRUE;
          294                 swap(&rect->y0, &rect->y1);
          295                 rect->x0 = rect->x1;
          296                 rect->x1 = x;
          297         } else if (collide_line(x, y, x1, y1, x0, y1)) {
          298                 state->lock_y = TRUE;
          299                 rect->y1 = y;
          300         } else if (collide_line(x, y, x1, y1, x1, y0)) {
          301                 state->lock_x = TRUE;
          302                 rect->x1 = x;
          303         } else if (collide_rect(x, y, *rect)) {
          304                 state->moving = TRUE;
          305                 state->move_handle.x = x;
          306                 state->move_handle.y = y;
          307         } else {
          308                 rect->x0 = x;
          309                 rect->y0 = y;
          310                 rect->x1 = x;
          311                 rect->y1 = y;
          312         }
          313         state->resizing = TRUE;
          314         return FALSE;
          315 }
          316 
          317 static gboolean
          318 button_release(GtkWidget *area, GdkEventButton *event, gpointer data) {
          319         struct State *state = (struct State *)data;
          320         state->moving = FALSE;
          321         state->resizing = FALSE;
          322         state->lock_x = FALSE;
          323         state->lock_y = FALSE;
          324         return FALSE;
          325 }
          326 
          327 static void
          328 redraw(GtkWidget *area, struct State *state) {
          329         if (!state->cur_pixbuf)
          330                 return;
          331         cairo_t *cr;
          332         cr = gdk_cairo_create(area->window);
          333 
          334         gdk_cairo_set_source_pixbuf(cr, state->cur_pixbuf, 0, 0);
          335         cairo_paint(cr);
          336 
          337         GdkColor col = state->cur_col == 1 ? state->col1 : state->col2;
          338         if (state->selections[state->cur_selection]) {
          339                 struct Rect rect = state->selections[state->cur_selection]->rect;
          340                 gdk_cairo_set_source_color(cr, &col);
          341                 cairo_move_to(cr, rect.x0, rect.y0);
          342                 cairo_line_to(cr, rect.x1, rect.y0);
          343                 cairo_line_to(cr, rect.x1, rect.y1);
          344                 cairo_line_to(cr, rect.x0, rect.y1);
          345                 cairo_line_to(cr, rect.x0, rect.y0);
          346                 cairo_stroke(cr);
          347         }
          348 
          349         cairo_destroy(cr);
          350 }
          351 
          352 static gboolean
          353 configure_event(GtkWidget *area, GdkEvent *event, gpointer data) {
          354         struct State *state = (struct State *)data;
          355         state->window_w = event->configure.width;
          356         state->window_h = event->configure.height;
          357         if (state->cur_selection == -1 && state->window_w > 0 && state->window_h > 0) {
          358                 next_picture(area, state, FALSE);
          359         }
          360         return FALSE;
          361 }
          362 
          363 static gboolean
          364 draw_expose(GtkWidget *area, GdkEvent *event, gpointer data) {
          365         struct State *state = (struct State *)data;
          366         if (state->cur_selection < 0)
          367                 return FALSE;
          368         redraw(area, state);
          369         return FALSE;
          370 }
          371 
          372 static gboolean
          373 drag_motion(GtkWidget *area, GdkEventMotion *event, gpointer data) {
          374         struct State *state = (struct State *)data;
          375         if (state->cur_selection < 0 || !state->selections[state->cur_selection])
          376                 return FALSE;
          377         struct Rect *rect = &state->selections[state->cur_selection]->rect;
          378         gint x = event->x;
          379         gint y = event->y;
          380         if (state->moving == TRUE) {
          381                 int x_delta = x - state->move_handle.x;
          382                 int y_delta = y - state->move_handle.y;
          383                 rect->x0 += x_delta;
          384                 rect->y0 += y_delta;
          385                 rect->x1 += x_delta;
          386                 rect->y1 += y_delta;
          387                 state->move_handle.x = x;
          388                 state->move_handle.y = y;
          389         } else if (state->resizing == TRUE) {
          390                 if (state->lock_y != TRUE)
          391                         rect->x1 = x;
          392                 if (state->lock_x != TRUE)
          393                         rect->y1 = y;
          394         } else {
          395                 int x0 = rect->x0, x1 = rect->x1;
          396                 int y0 = rect->y0, y1 = rect->y1;
          397                 sort_coordinates(&x0, &y0, &x1, &y1);
          398                 GdkCursor *c = NULL;
          399                 GdkCursor *old = gdk_window_get_cursor(area->window);
          400                 if (old)
          401                         gdk_cursor_unref(old);
          402                 if (collide_point(x, y, x0, y0)) {
          403                         c = gdk_cursor_new(GDK_TOP_LEFT_CORNER);
          404                 } else if (collide_point(x, y, x1, y0)) {
          405                         c = gdk_cursor_new(GDK_TOP_RIGHT_CORNER);
          406                 } else if (collide_point(x, y, x0, y1)) {
          407                         c = gdk_cursor_new(GDK_BOTTOM_LEFT_CORNER);
          408                 } else if (collide_point(x, y, x1, y1)) {
          409                         c = gdk_cursor_new(GDK_BOTTOM_RIGHT_CORNER);
          410                 } else if (collide_line(x, y, x0, y0, x1, y0)) {
          411                         c = gdk_cursor_new(GDK_TOP_SIDE);
          412                 } else if (collide_line(x, y, x1, y1, x0, y1)) {
          413                         c = gdk_cursor_new(GDK_BOTTOM_SIDE);
          414                 } else if (collide_line(x, y, x1, y1, x1, y0)) {
          415                         c = gdk_cursor_new(GDK_RIGHT_SIDE);
          416                 } else if (collide_line(x, y, x0, y0, x0, y1)) {
          417                         c = gdk_cursor_new(GDK_LEFT_SIDE);
          418                 } else if (collide_rect(x, y, *rect)) {
          419                         c = gdk_cursor_new(GDK_FLEUR);
          420                 }
          421                 gdk_window_set_cursor(area->window, c);
          422                 return FALSE;
          423         }
          424 
          425         gtk_widget_queue_draw(area);
          426         return FALSE;
          427 }
          428 
          429 static struct Selection *
          430 create_selection(
          431         int rect_x0, int rect_y0, int rect_x1, int rect_y1,
          432         int orig_w, int orig_h, int scaled_w, int scaled_h) {
          433 
          434         struct Selection *sel = malloc(sizeof(struct Selection));
          435         sel->rect.x0 = rect_x0;
          436         sel->rect.y0 = rect_y0;
          437         sel->rect.x1 = rect_x1;
          438         sel->rect.y1 = rect_y1;
          439         sel->orig_w = orig_w;
          440         sel->orig_h = orig_h;
          441         sel->scaled_w = scaled_w;
          442         sel->scaled_h = scaled_h;
          443         return sel;
          444 }
          445 
          446 static void
          447 change_picture(
          448         GtkWidget *area, GdkPixbuf *new_pixbuf,
          449         int new_selection, int orig_w, int orig_h,
          450         struct State *state, gboolean copy_box) {
          451 
          452         if (state->cur_pixbuf) {
          453                 g_object_unref(G_OBJECT(state->cur_pixbuf));
          454                 state->cur_pixbuf = NULL;
          455         }
          456         state->cur_pixbuf = new_pixbuf;
          457         int old_selection = state->cur_selection;
          458         state->cur_selection = new_selection;
          459 
          460         struct Selection *sel = state->selections[state->cur_selection];
          461         int actual_w = gdk_pixbuf_get_width(state->cur_pixbuf);
          462         int actual_h = gdk_pixbuf_get_height(state->cur_pixbuf);
          463         if (copy_box == TRUE && old_selection >= 0 && old_selection < state->num_files) {
          464                 struct Selection *old = state->selections[old_selection];
          465                 if (sel)
          466                         free(sel);
          467                 sel = create_selection(old->rect.x0, old->rect.y0, old->rect.x1, old->rect.y1,
          468                         orig_w, orig_h, actual_w, actual_h);
          469         } else if (!sel) {
          470                 /* Just fill it with -200 so we can check later if it has been used yet */
          471                 sel = create_selection(-200, -200, -200, -200, orig_w, orig_h, actual_w, actual_h);
          472         } else if (sel->rect.x0 != -200 && actual_w != sel->scaled_w) {
          473                 /* If there is a selection, we need to convert it to the new scale.
          474                  * This only takes width into account because the aspect ratio
          475                  * should have been preserved anyways */
          476                 double scale = (double)actual_w / sel->scaled_w;
          477                 sel->rect.x0 = round(sel->rect.x0 * scale);
          478                 sel->rect.y0 = round(sel->rect.y0 * scale);
          479                 sel->rect.x1 = round(sel->rect.x1 * scale);
          480                 sel->rect.y1 = round(sel->rect.y1 * scale);
          481         }
          482         sel->scaled_w = actual_w;
          483         sel->scaled_h = actual_h;
          484         state->selections[state->cur_selection] = sel;
          485         gtk_widget_queue_draw(area);
          486 }
          487 
          488 static void
          489 next_picture(GtkWidget *area, struct State *state, gboolean copy_box) {
          490         if (state->cur_selection + 1 >= state->num_files)
          491                 return;
          492         GdkPixbuf *tmp_pixbuf = NULL;
          493         int tmp_cur_selection = state->cur_selection;
          494         int orig_w, orig_h;
          495         /* loop until we find a loadable file */
          496         while (!tmp_pixbuf && tmp_cur_selection + 1 < state->num_files) {
          497                 tmp_cur_selection++;
          498                 tmp_pixbuf = load_pixbuf(
          499                     state->filenames[tmp_cur_selection],
          500                     state->window_w, state->window_h, &orig_w, &orig_h);
          501         }
          502         if (!tmp_pixbuf)
          503                 return;
          504         change_picture(area, tmp_pixbuf, tmp_cur_selection, orig_w, orig_h, state, copy_box);
          505 }
          506 
          507 static void
          508 last_picture(GtkWidget *area, struct State *state) {
          509         if (state->cur_selection <= 0)
          510                 return;
          511         GdkPixbuf *tmp_pixbuf = NULL;
          512         int tmp_cur_selection = state->cur_selection;
          513         int orig_w, orig_h;
          514         /* loop until we find a loadable file */
          515         while (!tmp_pixbuf && tmp_cur_selection > 0) {
          516                 tmp_cur_selection--;
          517                 tmp_pixbuf = load_pixbuf(
          518                     state->filenames[tmp_cur_selection],
          519                     state->window_w, state->window_h, &orig_w, &orig_h);
          520         }
          521 
          522         if (!tmp_pixbuf)
          523                 return;
          524         change_picture(area, tmp_pixbuf, tmp_cur_selection, orig_w, orig_h, state, FALSE);
          525 }
          526 
          527 static void
          528 clear_selection(GtkWidget *area, struct State *state) {
          529         if (state->cur_selection < 0 || !state->selections[state->cur_selection])
          530                 return;
          531         struct Selection *sel = state->selections[state->cur_selection];
          532         sel->rect.x0 = sel->rect.x1 = sel->rect.y0 = sel->rect.y1 = -200;
          533         gtk_widget_queue_draw(area);
          534 }
          535 
          536 static void
          537 resize_manual(GtkWidget *area, struct State *state) {
          538         if (state->cur_selection < 0 || !state->selections[state->cur_selection])
          539                 return;
          540         int orig_w, orig_h;
          541         GdkPixbuf *tmp_pixbuf = load_pixbuf(
          542             state->filenames[state->cur_selection],
          543             state->window_w, state->window_h, &orig_w, &orig_h);
          544         if (!tmp_pixbuf)
          545                 return;
          546         change_picture(area, tmp_pixbuf, state->cur_selection, orig_w, orig_h, state, FALSE);
          547 }
          548 
          549 static void
          550 switch_color(GtkWidget *area, struct State *state) {
          551         if (state->cur_selection < 0 || !state->selections[state->cur_selection])
          552                 return;
          553         state->cur_col = state->cur_col == 1 ? 2 : 1;
          554         gtk_widget_queue_draw(area);
          555 }
          556 
          557 static gboolean
          558 key_press(GtkWidget *area, GdkEventKey *event, gpointer data) {
          559         struct State *state = (struct State *)data;
          560         switch (event->keyval) {
          561         case GDK_KEY_Left:
          562                 last_picture(area, state);
          563                 break;
          564         case GDK_KEY_Right:
          565                 next_picture(area, state, FALSE);
          566                 break;
          567         case GDK_KEY_Return:
          568                 next_picture(area, state, TRUE);
          569                 break;
          570         case GDK_KEY_Delete:
          571                 clear_selection(area, state);
          572                 break;
          573         case GDK_KEY_space:
          574                 resize_manual(area, state);
          575                 break;
          576         case GDK_KEY_Tab:
          577                 switch_color(area, state);
          578                 break;
          579         }
          580         return FALSE;
          581 }