1 /***************************************************************************
2  *   Copyright (C) 2007, 2008 by PCMan (Hong Jen Yee)                      *
3  *   pcman.tw@gmail.com                                                    *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20 
21 #ifdef HAVE_CONFIG_H
22 #  include <config.h>
23 #endif
24 
25 #include "main-win.h"
26 
27 #include <glib/gi18n.h>
28 #include <glib/gstdio.h>
29 #include <gdk/gdkkeysyms.h>
30 #include <gdk/gdkkeysyms-compat.h>
31 
32 #include <unistd.h>
33 #include <string.h>
34 #include <errno.h>
35 #include <stdlib.h>
36 #include <stdio.h>
37 
38 #include "pref.h"
39 
40 #include "image-view.h"
41 #include "image-list.h"
42 #include "working-area.h"
43 #include "ptk-menu.h"
44 #include "file-dlgs.h"
45 #include "jpeg-tran.h"
46 
47 /* For drag & drop */
48 static GtkTargetEntry drop_targets[] =
49 {
50     {"text/uri-list", 0, 0},
51     {"text/plain", 0, 1}
52 };
53 
54 extern int ExifRotate(const char * fname, int new_angle);
55 // defined in exif.c
56 extern int ExifRotateFlipMapping[9][9];
57 
58 static void main_win_init( MainWin*mw );
59 static void main_win_finalize( GObject* obj );
60 
61 static void create_nav_bar( MainWin* mw, GtkWidget* box);
62 GtkWidget* add_nav_btn( MainWin* mw, const char* icon, const char* tip, GCallback cb, gboolean toggle);
63 GtkWidget* add_nav_btn_img( MainWin* mw, const char* icon, const char* tip, GCallback cb, gboolean toggle, GtkWidget** ret_img);
64 // GtkWidget* add_menu_item(  GtkMenuShell* menu, const char* label, const char* icon, GCallback cb, gboolean toggle=FALSE );
65 static void rotate_image( MainWin* mw, int angle );
66 static void show_popup_menu( MainWin* mw, GdkEventButton* evt );
67 
68 /* signal handlers */
69 static gboolean on_delete_event( GtkWidget* widget, GdkEventAny* evt );
70 static void on_size_allocate( GtkWidget* widget, GtkAllocation    *allocation );
71 static gboolean on_win_state_event( GtkWidget* widget, GdkEventWindowState* state );
72 static void on_scroll_size_allocate(GtkWidget* widget, GtkAllocation* allocation, MainWin* mv);
73 static void on_zoom_fit( GtkToggleButton* btn, MainWin* mw );
74 static void on_zoom_fit_menu( GtkMenuItem* item, MainWin* mw );
75 static void on_full_screen( GtkWidget* btn, MainWin* mw );
76 static void on_next( GtkWidget* btn, MainWin* mw );
77 static void on_orig_size( GtkToggleButton* btn, MainWin* mw );
78 static void on_orig_size_menu( GtkToggleButton* btn, MainWin* mw );
79 static void on_prev( GtkWidget* btn, MainWin* mw );
80 static void on_rotate_auto_save( GtkWidget* btn, MainWin* mw );
81 static void on_rotate_clockwise( GtkWidget* btn, MainWin* mw );
82 static void on_rotate_counterclockwise( GtkWidget* btn, MainWin* mw );
83 static void on_save_as( GtkWidget* btn, MainWin* mw );
84 static void on_save( GtkWidget* btn, MainWin* mw );
85 static void cancel_slideshow(MainWin* mw);
86 static gboolean next_slide(MainWin* mw);
87 static void on_slideshow_menu( GtkMenuItem* item, MainWin* mw );
88 static void on_slideshow( GtkToggleButton* btn, MainWin* mw );
89 static void on_open( GtkWidget* btn, MainWin* mw );
90 static void on_zoom_in( GtkWidget* btn, MainWin* mw );
91 static void on_zoom_out( GtkWidget* btn, MainWin* mw );
92 static void on_preference( GtkWidget* btn, MainWin* mw );
93 static void on_toggle_toolbar( GtkMenuItem* item, MainWin* mw );
94 static void on_quit( GtkWidget* btn, MainWin* mw );
95 static gboolean on_button_press( GtkWidget* widget, GdkEventButton* evt, MainWin* mw );
96 static gboolean on_button_release( GtkWidget* widget, GdkEventButton* evt, MainWin* mw );
97 static gboolean on_mouse_move( GtkWidget* widget, GdkEventMotion* evt, MainWin* mw );
98 static gboolean on_scroll_event( GtkWidget* widget, GdkEventScroll* evt, MainWin* mw );
99 static gboolean on_key_press_event(GtkWidget* widget, GdkEventKey * key);
100 static gboolean save_confirm( MainWin* mw, const char* file_path );
101 static void on_drag_data_received( GtkWidget* widget, GdkDragContext *drag_context,
102                 int x, int y, GtkSelectionData* data, guint info, guint time, MainWin* mw );
103 static void on_delete( GtkWidget* btn, MainWin* mw );
104 static void on_about( GtkWidget* menu, MainWin* mw );
105 static gboolean on_animation_timeout( MainWin* mw );
106 
107 static void update_title(const char *filename, MainWin *mw );
108 
109 void on_flip_vertical( GtkWidget* btn, MainWin* mw );
110 void on_flip_horizontal( GtkWidget* btn, MainWin* mw );
111 static int trans_angle_to_id(int i);
112 static int get_new_angle( int orig_angle, int rotate_angle );
113 
114 static void main_win_set_zoom_scale(MainWin* mw, double scale);
115 static void main_win_set_zoom_mode(MainWin* mw, ZoomMode mode);
116 static void main_win_update_zoom_buttons_state(MainWin* mw);
117 
118 // Begin of GObject-related stuff
119 
G_DEFINE_TYPE(MainWin,main_win,GTK_TYPE_WINDOW)120 G_DEFINE_TYPE( MainWin, main_win, GTK_TYPE_WINDOW )
121 
122 void main_win_class_init( MainWinClass* klass )
123 {
124     GObjectClass * obj_class;
125     GtkWidgetClass *widget_class;
126 
127     obj_class = ( GObjectClass * ) klass;
128 //    obj_class->set_property = _set_property;
129 //   obj_class->get_property = _get_property;
130     obj_class->finalize = main_win_finalize;
131 
132     widget_class = GTK_WIDGET_CLASS ( klass );
133     widget_class->delete_event = on_delete_event;
134     widget_class->size_allocate = on_size_allocate;
135     widget_class->key_press_event = on_key_press_event;
136     widget_class->window_state_event = on_win_state_event;
137 }
138 
main_win_finalize(GObject * obj)139 void main_win_finalize( GObject* obj )
140 {
141     MainWin *mw = (MainWin*)obj;
142 
143     main_win_close(mw);
144 
145     if( G_LIKELY(mw->img_list) )
146         image_list_free( mw->img_list );
147 #if GTK_CHECK_VERSION(3, 0, 0)
148     g_object_unref( mw->hand_cursor );
149 #else
150     gdk_cursor_unref( mw->hand_cursor );
151 #endif
152     // FIXME: Put this here is weird
153     gtk_main_quit();
154 }
155 
main_win_new()156 GtkWidget* main_win_new()
157 {
158     return (GtkWidget*)g_object_new ( MAIN_WIN_TYPE, NULL );
159 }
160 
161 // End of GObject-related stuff
162 
main_win_init(MainWin * mw)163 void main_win_init( MainWin*mw )
164 {
165     gtk_window_set_title( (GtkWindow*)mw, _("Image Viewer"));
166     if (gtk_icon_theme_has_icon(gtk_icon_theme_get_default(), "gpicview"))
167     {
168         gtk_window_set_icon_name((GtkWindow*)mw, "gpicview");
169     }
170     else
171     {
172         gtk_window_set_icon_from_file((GtkWindow*)mw, PACKAGE_DATA_DIR "/icons/hicolor/48x48/apps/gpicview.png", NULL);
173     }
174     gtk_window_set_default_size( (GtkWindow*)mw, 640, 480 );
175 
176 #if GTK_CHECK_VERSION(3, 0, 0)
177     GtkWidget* box = gtk_box_new( GTK_ORIENTATION_VERTICAL, 0 );
178 #else
179     GtkWidget* box = gtk_vbox_new( FALSE, 0 );
180 #endif
181     gtk_container_add( (GtkContainer*)mw, box);
182 
183     // image area
184     mw->evt_box = gtk_event_box_new();
185 #if GTK_CHECK_VERSION(2, 18, 0)
186     gtk_widget_set_can_focus(mw->evt_box,TRUE);
187 #else
188     GTK_WIDGET_SET_FLAGS( mw->evt_box, GTK_CAN_FOCUS );
189 #endif
190     gtk_widget_add_events( mw->evt_box,
191                            GDK_POINTER_MOTION_MASK|GDK_BUTTON_PRESS_MASK|
192                            GDK_BUTTON_RELEASE_MASK|GDK_SCROLL_MASK );
193     g_signal_connect( mw->evt_box, "button-press-event", G_CALLBACK(on_button_press), mw );
194     g_signal_connect( mw->evt_box, "button-release-event", G_CALLBACK(on_button_release), mw );
195     g_signal_connect( mw->evt_box, "motion-notify-event", G_CALLBACK(on_mouse_move), mw );
196     g_signal_connect( mw->evt_box, "scroll-event", G_CALLBACK(on_scroll_event), mw );
197     // Set bg color to white
198 
199     gtk_widget_modify_bg( mw->evt_box, GTK_STATE_NORMAL, &pref.bg );
200 
201     mw->img_view = image_view_new();
202     gtk_container_add( (GtkContainer*)mw->evt_box, (GtkWidget*)mw->img_view);
203 
204 #if GTK_CHECK_VERSION(3, 0, 0)
205 #else
206     const char scroll_style[]=
207             "style \"gpicview-scroll\" {"
208             "GtkScrolledWindow::scrollbar-spacing=0"
209             "}"
210             "class \"GtkScrolledWindow\" style \"gpicview-scroll\"";
211     gtk_rc_parse_string( scroll_style );
212 #endif
213     mw->scroll = gtk_scrolled_window_new( NULL, NULL );
214     g_signal_connect(G_OBJECT(mw->scroll), "size-allocate", G_CALLBACK(on_scroll_size_allocate), (gpointer) mw);
215     gtk_scrolled_window_set_shadow_type( (GtkScrolledWindow*)mw->scroll, GTK_SHADOW_NONE );
216     gtk_scrolled_window_set_policy((GtkScrolledWindow*)mw->scroll,
217                                     GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
218     GtkAdjustment *hadj, *vadj;
219     hadj = gtk_scrolled_window_get_hadjustment((GtkScrolledWindow*)mw->scroll);
220 #if GTK_CHECK_VERSION(2, 14, 0)
221     gtk_adjustment_set_page_increment(hadj, 10);
222 #else
223     hadj->page_increment = 10;
224 #endif
225     gtk_adjustment_changed(hadj);
226     vadj = gtk_scrolled_window_get_vadjustment((GtkScrolledWindow*)mw->scroll);
227 #if GTK_CHECK_VERSION(2, 14, 0)
228     gtk_adjustment_set_page_increment(vadj, 10);
229 #else
230     vadj->page_increment = 10;
231 #endif
232     gtk_adjustment_changed(vadj);
233 
234     image_view_set_adjustments( IMAGE_VIEW(mw->img_view), hadj, vadj );    // dirty hack :-(
235     gtk_scrolled_window_add_with_viewport( (GtkScrolledWindow*)mw->scroll, mw->evt_box );
236     GtkWidget* viewport = gtk_bin_get_child( (GtkBin*)mw->scroll );
237     gtk_viewport_set_shadow_type( (GtkViewport*)viewport, GTK_SHADOW_NONE );
238     gtk_container_set_border_width( (GtkContainer*)viewport, 0 );
239 
240     gtk_box_pack_start( (GtkBox*)box, mw->scroll, TRUE, TRUE, 0 );
241 
242     // build toolbar
243     create_nav_bar( mw, box );
244     gtk_widget_show_all( box );
245 
246     if (pref.show_toolbar)
247         gtk_widget_show(gtk_widget_get_parent(mw->nav_bar));
248     else
249         gtk_widget_hide(gtk_widget_get_parent(mw->nav_bar));
250 
251 
252     mw->hand_cursor = gdk_cursor_new_for_display( gtk_widget_get_display((GtkWidget*)mw), GDK_FLEUR );
253 
254 //    zoom_mode = ZOOM_NONE;
255     mw->zoom_mode = ZOOM_FIT;
256 
257     // Set up drag & drop
258     gtk_drag_dest_set( (GtkWidget*)mw, GTK_DEST_DEFAULT_ALL,
259                                                     drop_targets,
260                                                     G_N_ELEMENTS(drop_targets),
261                                                     GDK_ACTION_COPY | GDK_ACTION_ASK );
262     g_signal_connect( mw, "drag-data-received", G_CALLBACK(on_drag_data_received), mw );
263 
264     mw->img_list = image_list_new();
265 
266     // rotation angle is zero on startup
267     mw->rotation_angle = 0;
268 }
269 
create_nav_bar(MainWin * mw,GtkWidget * box)270 void create_nav_bar( MainWin* mw, GtkWidget* box )
271 {
272     mw->nav_bar = gtk_hbox_new( FALSE, 0 );
273 
274     add_nav_btn( mw, GTK_STOCK_GO_BACK, _("Previous"), G_CALLBACK(on_prev), FALSE );
275     add_nav_btn( mw, GTK_STOCK_GO_FORWARD, _("Next"), G_CALLBACK(on_next), FALSE );
276     mw->btn_play_stop = add_nav_btn_img( mw, GTK_STOCK_MEDIA_PLAY, _("Start Slideshow"), G_CALLBACK(on_slideshow), TRUE, &mw->img_play_stop );
277 
278     gtk_box_pack_start( (GtkBox*)mw->nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
279 
280     add_nav_btn( mw, GTK_STOCK_ZOOM_OUT, _("Zoom Out"), G_CALLBACK(on_zoom_out), FALSE );
281     add_nav_btn( mw, GTK_STOCK_ZOOM_IN, _("Zoom In"), G_CALLBACK(on_zoom_in), FALSE );
282 
283 //    percent = gtk_entry_new();    // show scale (in percentage)
284 //    g_signal_connect( percent, "activate", G_CALLBACK(on_percentage), mw );
285 //    gtk_widget_set_size_request( percent, 45, -1 );
286 //    gtk_box_pack_start( (GtkBox*)nav_bar, percent, FALSE, FALSE, 2 );
287 
288     mw->btn_fit = add_nav_btn( mw, GTK_STOCK_ZOOM_FIT, _("Fit Image To Window Size"),
289                            G_CALLBACK(on_zoom_fit), TRUE );
290     mw->btn_orig = add_nav_btn( mw, GTK_STOCK_ZOOM_100, _("Original Size"),
291                            G_CALLBACK(on_orig_size), TRUE );
292     gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_fit, TRUE );
293 
294 #ifndef GTK_STOCK_FULLSCREEN
295 #define GTK_STOCK_FULLSCREEN    "gtk-fullscreen"
296 #endif
297     add_nav_btn( mw, GTK_STOCK_FULLSCREEN, _("Full Screen"), G_CALLBACK(on_full_screen), FALSE );   // gtk+ 2.8+
298 
299     gtk_box_pack_start( (GtkBox*)mw->nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
300 
301     mw->btn_rotate_ccw = add_nav_btn( mw, "object-rotate-left", _("Rotate Counterclockwise"), G_CALLBACK(on_rotate_counterclockwise), FALSE );
302     mw->btn_rotate_cw = add_nav_btn( mw, "object-rotate-right", _("Rotate Clockwise"), G_CALLBACK(on_rotate_clockwise), FALSE );
303 
304     mw->btn_flip_h = add_nav_btn( mw, "object-flip-horizontal", _("Flip Horizontal"), G_CALLBACK(on_flip_horizontal), FALSE );
305     mw->btn_flip_v = add_nav_btn( mw, "object-flip-vertical", _("Flip Vertical"), G_CALLBACK(on_flip_vertical), FALSE );
306 
307     gtk_box_pack_start( (GtkBox*)mw->nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
308 
309     add_nav_btn( mw, GTK_STOCK_OPEN, _("Open File"), G_CALLBACK(on_open), FALSE );
310     add_nav_btn( mw, GTK_STOCK_SAVE, _("Save File"), G_CALLBACK(on_save), FALSE );
311     add_nav_btn( mw, GTK_STOCK_SAVE_AS, _("Save File As"), G_CALLBACK(on_save_as), FALSE );
312     add_nav_btn( mw, GTK_STOCK_DELETE, _("Delete File"), G_CALLBACK(on_delete), FALSE );
313 
314     gtk_box_pack_start( (GtkBox*)mw->nav_bar, gtk_vseparator_new(), FALSE, FALSE, 0 );
315     add_nav_btn( mw, GTK_STOCK_PREFERENCES, _("Preferences"), G_CALLBACK(on_preference), FALSE );
316     add_nav_btn( mw, GTK_STOCK_QUIT, _("Quit"), G_CALLBACK(on_quit), FALSE );
317 
318     GtkWidget* align = gtk_alignment_new( 0.5, 0, 0, 0 );
319     gtk_container_add( (GtkContainer*)align, mw->nav_bar );
320     gtk_box_pack_start( (GtkBox*)box, align, FALSE, TRUE, 2 );
321 }
322 
on_delete_event(GtkWidget * widget,GdkEventAny * evt)323 gboolean on_delete_event( GtkWidget* widget, GdkEventAny* evt )
324 {
325     gtk_widget_destroy( widget );
326     return TRUE;
327 }
328 
update_title(const char * filename,MainWin * mw)329 static void update_title(const char *filename, MainWin *mw )
330 {
331     static char fname[50];
332     static int wid, hei;
333 
334     char buf[100];
335 
336     if(filename != NULL)
337     {
338       strncpy(fname, filename, 49);
339       fname[49] = '\0';
340 
341       wid = gdk_pixbuf_get_width( mw->pix );
342       hei = gdk_pixbuf_get_height( mw->pix );
343     }
344 
345     snprintf(buf, 100, "%s (%dx%d) %d%%", fname, wid, hei, (int)(mw->scale * 100));
346     gtk_window_set_title( (GtkWindow*)mw, buf );
347 
348     return;
349 }
350 
on_animation_timeout(MainWin * mw)351 gboolean on_animation_timeout( MainWin* mw )
352 {
353     int delay;
354     if ( gdk_pixbuf_animation_iter_advance( mw->animation_iter, NULL ) )
355     {
356         mw->pix = gdk_pixbuf_animation_iter_get_pixbuf( mw->animation_iter );
357         image_view_set_pixbuf( (ImageView*)mw->img_view, mw->pix );
358     }
359     delay = gdk_pixbuf_animation_iter_get_delay_time( mw->animation_iter );
360     mw->animation_timeout = g_timeout_add(delay, (GSourceFunc) on_animation_timeout, mw );
361     return FALSE;
362 }
363 
update_btns(MainWin * mw)364 static void update_btns(MainWin* mw)
365 {
366     gboolean enable = (mw->animation == NULL);
367     gtk_widget_set_sensitive(mw->btn_rotate_cw, enable);
368     gtk_widget_set_sensitive(mw->btn_rotate_ccw, enable);
369     gtk_widget_set_sensitive(mw->btn_flip_v, enable);
370     gtk_widget_set_sensitive(mw->btn_flip_h, enable);
371 }
372 
main_win_open(MainWin * mw,const char * file_path,ZoomMode zoom)373 gboolean main_win_open( MainWin* mw, const char* file_path, ZoomMode zoom )
374 {
375     if (g_file_test(file_path, G_FILE_TEST_IS_DIR))
376     {
377         image_list_open_dir( mw->img_list, file_path, NULL );
378         image_list_sort_by_name( mw->img_list, GTK_SORT_DESCENDING );
379         if (image_list_get_first(mw->img_list))
380             main_win_open(mw, image_list_get_current_file_path(mw->img_list), zoom);
381         return TRUE;
382     }
383 
384 
385     GError* err = NULL;
386     GdkPixbufFormat* info;
387     info = gdk_pixbuf_get_file_info( file_path, NULL, NULL );
388     char* type = ((info != NULL) ? gdk_pixbuf_format_get_name(info) : "");
389 
390     main_win_close( mw );
391 
392     /* grabs a file as if it were an animation */
393     mw->animation = gdk_pixbuf_animation_new_from_file( file_path, &err );
394     if( ! mw->animation )
395     {
396         main_win_show_error( mw, err->message );
397         g_error_free(err);
398         update_btns( mw );
399         return FALSE;
400     }
401 
402     /* tests if the file is actually just a normal picture */
403     if ( gdk_pixbuf_animation_is_static_image( mw->animation ) )
404     {
405        mw->pix = gdk_pixbuf_animation_get_static_image( mw->animation );
406        g_object_ref(mw->pix);
407        g_object_unref(mw->animation);
408        mw->animation = NULL;
409     }
410     else
411     {
412         int delay;
413         /* we found an animation */
414         mw->animation_iter = gdk_pixbuf_animation_get_iter( mw->animation, NULL );
415         mw->pix = gdk_pixbuf_animation_iter_get_pixbuf( mw->animation_iter );
416         delay = gdk_pixbuf_animation_iter_get_delay_time( mw->animation_iter );
417         mw->animation_timeout = g_timeout_add( delay, (GSourceFunc) on_animation_timeout, mw );
418     }
419     update_btns( mw );
420 
421     if(!strcmp(type,"jpeg"))
422     {
423         GdkPixbuf* tmp;
424         // Only jpeg should rotate by EXIF
425         tmp = gdk_pixbuf_apply_embedded_orientation(mw->pix);
426         g_object_unref(mw->pix);
427         mw->pix = tmp;
428     }
429 
430     mw->zoom_mode = zoom;
431 
432     // select most suitable viewing mode
433     if( zoom == ZOOM_NONE )
434     {
435         int w = gdk_pixbuf_get_width( mw->pix );
436         int h = gdk_pixbuf_get_height( mw->pix );
437 
438         GdkRectangle area;
439         get_working_area( gtk_widget_get_screen((GtkWidget*)mw), &area );
440         // g_debug("determine best zoom mode: orig size:  w=%d, h=%d", w, h);
441         // FIXME: actually this is a little buggy :-(
442         if( w < area.width && h < area.height && (w >= 640 || h >= 480) )
443         {
444             gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll, GTK_POLICY_NEVER, GTK_POLICY_NEVER );
445             gtk_widget_set_size_request( (GtkWidget*)mw->img_view, w, h );
446             GtkRequisition req;
447             gtk_widget_size_request ( (GtkWidget*)mw, &req );
448             if( req.width < 640 )   req.width = 640;
449             if( req.height < 480 )   req.height = 480;
450             gtk_window_resize( (GtkWindow*)mw, req.width, req.height );
451             gtk_widget_set_size_request( (GtkWidget*)mw->img_view, -1, -1 );
452             gtk_scrolled_window_set_policy( (GtkScrolledWindow*)mw->scroll, GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC );
453             mw->zoom_mode = ZOOM_ORIG;
454             mw->scale = 1.0;
455         }
456         else
457             mw->zoom_mode = ZOOM_FIT;
458     }
459 
460     if( mw->zoom_mode == ZOOM_FIT )
461     {
462         main_win_fit_window_size( mw, FALSE, GDK_INTERP_BILINEAR );
463     }
464     else  if( mw->zoom_mode == ZOOM_SCALE )  // scale
465     {
466         main_win_scale_image( mw, mw->scale, GDK_INTERP_BILINEAR );
467     }
468     else  if( mw->zoom_mode == ZOOM_ORIG )  // original size
469     {
470         image_view_set_scale( (ImageView*)mw->img_view, mw->scale, GDK_INTERP_BILINEAR );
471         main_win_center_image( mw );
472     }
473 
474     image_view_set_pixbuf( (ImageView*)mw->img_view, mw->pix );
475 
476 //    while (gtk_events_pending ())
477 //        gtk_main_iteration ();
478 
479     // build file list
480     char* dir_path = g_path_get_dirname( file_path );
481     image_list_open_dir( mw->img_list, dir_path, NULL );
482     image_list_sort_by_name( mw->img_list, GTK_SORT_DESCENDING );
483     g_free( dir_path );
484 
485     char* base_name = g_path_get_basename( file_path );
486     image_list_set_current( mw->img_list, base_name );
487 
488     char* disp_name = g_filename_display_name( base_name );
489     g_free( base_name );
490 
491     update_title( disp_name, mw );
492     g_free( disp_name );
493 
494     main_win_update_zoom_buttons_state(mw);
495 
496     return TRUE;
497 }
498 
main_win_start_slideshow(MainWin * mw)499 void main_win_start_slideshow( MainWin* mw )
500 {
501     on_slideshow_menu(NULL, mw);
502 }
503 
main_win_close(MainWin * mw)504 void main_win_close( MainWin* mw )
505 {
506     if( mw->animation )
507     {
508         g_object_unref( mw->animation );
509         mw->animation = NULL;
510         if( mw->animation_timeout );
511         {
512             g_source_remove( mw->animation_timeout );
513             mw->animation_timeout = 0;
514         }
515     }
516     else if( mw->pix )
517     {
518         g_object_unref( mw->pix );
519     }
520     mw->pix = NULL;
521 }
522 
main_win_show_error(MainWin * mw,const char * message)523 void main_win_show_error( MainWin* mw, const char* message )
524 {
525     GtkWidget* dlg = gtk_message_dialog_new( (GtkWindow*)mw,
526                                               GTK_DIALOG_MODAL,
527                                               GTK_MESSAGE_ERROR,
528                                               GTK_BUTTONS_OK,
529                                               "%s", message );
530     gtk_dialog_run( (GtkDialog*)dlg );
531     gtk_widget_destroy( dlg );
532 }
533 
on_size_allocate(GtkWidget * widget,GtkAllocation * allocation)534 void on_size_allocate( GtkWidget* widget, GtkAllocation    *allocation )
535 {
536     GTK_WIDGET_CLASS(main_win_parent_class)->size_allocate( widget, allocation );
537 #if GTK_CHECK_VERSION(2, 20, 0)
538     if(gtk_widget_get_realized (widget) )
539 #else
540     if( GTK_WIDGET_REALIZED (widget) )
541 #endif
542     {
543         MainWin* mw = (MainWin*)widget;
544 
545         if( mw->zoom_mode == ZOOM_FIT )
546         {
547             while(gtk_events_pending ())
548                 gtk_main_iteration(); // makes it more fluid
549 
550             main_win_fit_window_size( mw, FALSE, GDK_INTERP_BILINEAR );
551         }
552     }
553 }
554 
on_win_state_event(GtkWidget * widget,GdkEventWindowState * state)555 gboolean on_win_state_event( GtkWidget* widget, GdkEventWindowState* state )
556 {
557     MainWin* mw = (MainWin*)widget;
558     if( (state->new_window_state & GDK_WINDOW_STATE_FULLSCREEN) != 0 )
559     {
560         gtk_widget_modify_bg( mw->evt_box, GTK_STATE_NORMAL, &pref.bg_full );
561         gtk_widget_hide( gtk_widget_get_parent(mw->nav_bar) );
562         mw->full_screen = TRUE;
563     }
564     else
565     {
566         gtk_widget_modify_bg( mw->evt_box, GTK_STATE_NORMAL, &pref.bg );
567         if (pref.show_toolbar)
568             gtk_widget_show( gtk_widget_get_parent(mw->nav_bar) );
569         mw->full_screen = FALSE;
570     }
571 
572     int previous = pref.open_maximized;
573     pref.open_maximized = (state->new_window_state == GDK_WINDOW_STATE_MAXIMIZED);
574     if (previous != pref.open_maximized)
575         save_preferences();
576     return TRUE;
577 }
578 
main_win_fit_size(MainWin * mw,int width,int height,gboolean can_strech,GdkInterpType type)579 void main_win_fit_size( MainWin* mw, int width, int height, gboolean can_strech, GdkInterpType type )
580 {
581     if( ! mw->pix )
582         return;
583 
584     int orig_w = gdk_pixbuf_get_width( mw->pix );
585     int orig_h = gdk_pixbuf_get_height( mw->pix );
586 
587     if( can_strech || (orig_w > width || orig_h > height) )
588     {
589         gdouble xscale = ((gdouble)width) / orig_w;
590         gdouble yscale = ((gdouble)height)/ orig_h;
591         gdouble final_scale = xscale < yscale ? xscale : yscale;
592 
593         main_win_scale_image( mw, final_scale, type );
594     }
595     else    // use original size if the image is smaller than the window
596     {
597         mw->scale = 1.0;
598         image_view_set_scale( (ImageView*)mw->img_view, 1.0, type );
599 
600         update_title(NULL, mw);
601     }
602 }
603 
main_win_fit_window_size(MainWin * mw,gboolean can_strech,GdkInterpType type)604 void main_win_fit_window_size(  MainWin* mw, gboolean can_strech, GdkInterpType type )
605 {
606     mw->zoom_mode = ZOOM_FIT;
607 
608     if( mw->pix == NULL )
609         return;
610     main_win_fit_size( mw, mw->scroll_allocation.width, mw->scroll_allocation.height, can_strech, type );
611 }
612 
add_nav_btn(MainWin * mw,const char * icon,const char * tip,GCallback cb,gboolean toggle)613 GtkWidget* add_nav_btn( MainWin* mw, const char* icon, const char* tip, GCallback cb, gboolean toggle)
614 {
615     GtkWidget* unused;
616     return add_nav_btn_img(mw, icon, tip, cb, toggle, &unused);
617 }
618 
add_nav_btn_img(MainWin * mw,const char * icon,const char * tip,GCallback cb,gboolean toggle,GtkWidget ** ret_img)619 GtkWidget* add_nav_btn_img( MainWin* mw, const char* icon, const char* tip, GCallback cb, gboolean toggle, GtkWidget** ret_img )
620 {
621     GtkWidget* img;
622     if( g_str_has_prefix(icon, "gtk-") )
623         img = gtk_image_new_from_stock(icon, GTK_ICON_SIZE_SMALL_TOOLBAR);
624     else
625         img = gtk_image_new_from_icon_name(icon, GTK_ICON_SIZE_SMALL_TOOLBAR);
626     GtkWidget* btn;
627     if( G_UNLIKELY(toggle) )
628     {
629         btn = gtk_toggle_button_new();
630         g_signal_connect( btn, "toggled", cb, mw );
631     }
632     else
633     {
634         btn = gtk_button_new();
635         g_signal_connect( btn, "clicked", cb, mw );
636     }
637     gtk_button_set_relief( (GtkButton*)btn, GTK_RELIEF_NONE );
638     gtk_button_set_focus_on_click( (GtkButton*)btn, FALSE );
639     gtk_container_add( (GtkContainer*)btn, img );
640     gtk_widget_set_tooltip_text( btn, tip );
641     gtk_box_pack_start( (GtkBox*)mw->nav_bar, btn, FALSE, FALSE, 0 );
642     *ret_img = img;
643     return btn;
644 }
645 
on_scroll_size_allocate(GtkWidget * widget,GtkAllocation * allocation,MainWin * mv)646 void on_scroll_size_allocate(GtkWidget* widget, GtkAllocation* allocation, MainWin* mv)
647 {
648     mv->scroll_allocation = *allocation;
649 }
650 
on_zoom_fit_menu(GtkMenuItem * item,MainWin * mw)651 void on_zoom_fit_menu( GtkMenuItem* item, MainWin* mw )
652 {
653     gtk_button_clicked( (GtkButton*)mw->btn_fit );
654 }
655 
on_zoom_fit(GtkToggleButton * btn,MainWin * mw)656 void on_zoom_fit( GtkToggleButton* btn, MainWin* mw )
657 {
658     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(btn)))
659         main_win_set_zoom_mode(mw, ZOOM_FIT);
660 }
661 
on_full_screen(GtkWidget * btn,MainWin * mw)662 void on_full_screen( GtkWidget* btn, MainWin* mw )
663 {
664     if( ! mw->full_screen )
665         gtk_window_fullscreen( (GtkWindow*)mw );
666     else
667         gtk_window_unfullscreen( (GtkWindow*)mw );
668 }
669 
on_orig_size_menu(GtkToggleButton * btn,MainWin * mw)670 void on_orig_size_menu( GtkToggleButton* btn, MainWin* mw )
671 {
672     gtk_button_clicked( (GtkButton*)mw->btn_orig );
673 }
674 
on_orig_size(GtkToggleButton * btn,MainWin * mw)675 void on_orig_size( GtkToggleButton* btn, MainWin* mw )
676 {
677     // this callback could be called from activate signal of menu item.
678     if( GTK_IS_MENU_ITEM(btn) )
679     {
680         gtk_button_clicked( (GtkButton*)mw->btn_orig );
681         return;
682     }
683 
684     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(btn)))
685         main_win_set_zoom_mode(mw, ZOOM_ORIG);
686 }
687 
on_prev(GtkWidget * btn,MainWin * mw)688 void on_prev( GtkWidget* btn, MainWin* mw )
689 {
690     const char* name;
691     if( image_list_is_empty( mw->img_list ) )
692         return;
693 
694     name = image_list_get_prev( mw->img_list );
695 
696     if( ! name && image_list_has_multiple_files( mw->img_list ) )
697     {
698         // FIXME: need to ask user first?
699         name = image_list_get_last( mw->img_list );
700     }
701 
702     if( name )
703     {
704         char* file_path = image_list_get_current_file_path( mw->img_list );
705         main_win_open( mw, file_path, mw->zoom_mode );
706         g_free( file_path );
707     }
708 }
709 
on_next(GtkWidget * btn,MainWin * mw)710 void on_next( GtkWidget* btn, MainWin* mw )
711 {
712     if( image_list_is_empty( mw->img_list ) )
713         return;
714 
715     const char* name = image_list_get_next( mw->img_list );
716 
717     if( ! name && image_list_has_multiple_files( mw->img_list ) )
718     {
719         // FIXME: need to ask user first?
720         name = image_list_get_first( mw->img_list );
721     }
722 
723     if( name )
724     {
725         char* file_path = image_list_get_current_file_path( mw->img_list );
726         main_win_open( mw, file_path, mw->zoom_mode );
727         g_free( file_path );
728     }
729 }
730 
cancel_slideshow(MainWin * mw)731 void cancel_slideshow(MainWin* mw)
732 {
733     mw->slideshow_cancelled = TRUE;
734     mw->slideshow_running = FALSE;
735     if (mw->slide_timeout != 0)
736         g_source_remove(mw->slide_timeout);
737     gtk_toggle_button_set_active( GTK_TOGGLE_BUTTON(mw->btn_play_stop), FALSE );
738 }
739 
next_slide(MainWin * mw)740 gboolean next_slide(MainWin* mw)
741 {
742     /* Timeout causes an implicit "Next". */
743     if (mw->slideshow_running)
744         on_next( NULL, mw );
745 
746     return mw->slideshow_running;
747 }
748 
on_slideshow_menu(GtkMenuItem * item,MainWin * mw)749 void on_slideshow_menu( GtkMenuItem* item, MainWin* mw )
750 {
751     gtk_button_clicked( (GtkButton*)mw->btn_play_stop );
752 }
753 
on_slideshow(GtkToggleButton * btn,MainWin * mw)754 void on_slideshow( GtkToggleButton* btn, MainWin* mw )
755 {
756     if ((mw->slideshow_running) || (mw->slideshow_cancelled))
757     {
758         mw->slideshow_running = FALSE;
759         mw->slideshow_cancelled = FALSE;
760         gtk_image_set_from_stock( GTK_IMAGE(mw->img_play_stop), GTK_STOCK_MEDIA_PLAY, GTK_ICON_SIZE_SMALL_TOOLBAR );
761         gtk_widget_set_tooltip_text( GTK_WIDGET(btn), _("Start Slideshow") );
762         gtk_toggle_button_set_active( btn, FALSE );
763     }
764     else
765     {
766         gtk_toggle_button_set_active( btn, TRUE );
767         mw->slideshow_running = TRUE;
768         gtk_image_set_from_stock( GTK_IMAGE(mw->img_play_stop), GTK_STOCK_MEDIA_STOP, GTK_ICON_SIZE_SMALL_TOOLBAR );
769         gtk_widget_set_tooltip_text( GTK_WIDGET(btn), _("Stop Slideshow") );
770         mw->slide_timeout = g_timeout_add(1000 * pref.slide_delay, (GSourceFunc) next_slide, mw);
771     }
772 }
773 
774 //////////////////// rotate & flip
775 
trans_angle_to_id(int i)776 static int trans_angle_to_id(int i)
777 {
778     if(i == 0) 		return 1;
779     else if(i == 90)	return 6;
780     else if(i == 180)	return 3;
781     else if(i == 270)	return 8;
782     else if(i == -45)	return 7;
783     else if(i == -90)	return 2;
784     else if(i == -135)	return 5;
785     else     /* -180 */ return 4;
786 }
787 
get_new_angle(int orig_angle,int rotate_angle)788 static int get_new_angle( int orig_angle, int rotate_angle )
789 {
790     // defined in exif.c
791     static int angle_trans_back[] = {0, 0, -90, 180, -180, -135, 90, -45, 270};
792 
793     orig_angle = trans_angle_to_id(orig_angle);
794     rotate_angle = trans_angle_to_id(rotate_angle);
795 
796     return angle_trans_back[ ExifRotateFlipMapping[orig_angle][rotate_angle] ];
797 }
798 
on_rotate_auto_save(GtkWidget * btn,MainWin * mw)799 void on_rotate_auto_save( GtkWidget* btn, MainWin* mw )
800 {
801     if(pref.auto_save_rotated){
802 //      gboolean ask_before_save = pref.ask_before_save;
803 //      pref.ask_before_save = FALSE;
804         on_save(btn,mw);
805 //      pref.ask_before_save = ask_before_save;
806     }
807 }
808 
on_rotate_clockwise(GtkWidget * btn,MainWin * mw)809 void on_rotate_clockwise( GtkWidget* btn, MainWin* mw )
810 {
811     cancel_slideshow(mw);
812     rotate_image( mw, GDK_PIXBUF_ROTATE_CLOCKWISE );
813     mw->rotation_angle = get_new_angle(mw->rotation_angle, 90);
814     on_rotate_auto_save(btn, mw);
815 }
816 
on_rotate_counterclockwise(GtkWidget * btn,MainWin * mw)817 void on_rotate_counterclockwise( GtkWidget* btn, MainWin* mw )
818 {
819     cancel_slideshow(mw);
820     rotate_image( mw, GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE );
821     mw->rotation_angle = get_new_angle(mw->rotation_angle, 270);
822     on_rotate_auto_save(btn, mw);
823 }
824 
on_flip_vertical(GtkWidget * btn,MainWin * mw)825 void on_flip_vertical( GtkWidget* btn, MainWin* mw )
826 {
827     cancel_slideshow(mw);
828     rotate_image( mw, -180 );
829     mw->rotation_angle = get_new_angle(mw->rotation_angle, -180);
830     on_rotate_auto_save(btn, mw);
831 }
832 
on_flip_horizontal(GtkWidget * btn,MainWin * mw)833 void on_flip_horizontal( GtkWidget* btn, MainWin* mw )
834 {
835     cancel_slideshow(mw);
836     rotate_image( mw, -90 );
837     mw->rotation_angle = get_new_angle(mw->rotation_angle, -90);
838     on_rotate_auto_save(btn, mw);
839 }
840 
841 /* end of rotate & flip */
842 
on_save_as(GtkWidget * btn,MainWin * mw)843 void on_save_as( GtkWidget* btn, MainWin* mw )
844 {
845     char *file, *type;
846 
847     cancel_slideshow(mw);
848     if( ! mw->pix )
849         return;
850 
851     file = get_save_filename( GTK_WINDOW(mw), image_list_get_dir( mw->img_list ), &type );
852     if( file )
853     {
854         char* dir;
855         main_win_save( mw, file, type, TRUE );
856         dir = g_path_get_dirname(file);
857         const char* name = file + strlen(dir) + 1;
858 
859         if( strcmp( image_list_get_dir(mw->img_list), dir ) == 0 )
860         {
861             /* if the saved file is located in the same dir */
862             /* simply add it to image list */
863             image_list_add_sorted( mw->img_list, name, TRUE );
864         }
865         else /* otherwise reload the whole image list. */
866         {
867             /* switch to the dir containing the saved file. */
868             image_list_open_dir( mw->img_list, dir, NULL );
869         }
870         update_title( name, mw );
871         g_free( dir );
872         g_free( file );
873         g_free( type );
874     }
875 }
876 
on_save(GtkWidget * btn,MainWin * mw)877 void on_save( GtkWidget* btn, MainWin* mw )
878 {
879     cancel_slideshow(mw);
880     if( ! mw->pix )
881         return;
882 
883     char* file_name = g_build_filename( image_list_get_dir( mw->img_list ),
884                                         image_list_get_current( mw->img_list ), NULL );
885     GdkPixbufFormat* info;
886     info = gdk_pixbuf_get_file_info( file_name, NULL, NULL );
887     char* type = gdk_pixbuf_format_get_name( info );
888 
889     /* Confirm save if requested. */
890     if ((pref.ask_before_save) && ( ! save_confirm(mw, file_name)))
891         return;
892 
893     if(strcmp(type,"jpeg")==0)
894     {
895         if(!pref.rotate_exif_only || ExifRotate(file_name, mw->rotation_angle) == FALSE)
896         {
897             // hialan notes:
898             // ExifRotate retrun FALSE when
899             //   1. Can not read file
900             //   2. Exif do not have TAG_ORIENTATION tag
901             //   3. Format unknown
902             // And then we apply rotate_and_save_jpeg_lossless() ,
903             // the result would not effected by EXIF Orientation...
904 #ifdef HAVE_LIBJPEG
905             int status = rotate_and_save_jpeg_lossless(file_name,mw->rotation_angle);
906 	    if(status != 0)
907             {
908                 main_win_show_error( mw, g_strerror(status) );
909             }
910 #else
911             main_win_save( mw, file_name, type, pref.ask_before_save );
912 #endif
913         }
914     } else
915         main_win_save( mw, file_name, type, pref.ask_before_save );
916     mw->rotation_angle = 0;
917     g_free( file_name );
918     g_free( type );
919 }
920 
on_open(GtkWidget * btn,MainWin * mw)921 void on_open( GtkWidget* btn, MainWin* mw )
922 {
923     cancel_slideshow(mw);
924     char* file = get_open_filename( (GtkWindow*)mw, image_list_get_dir( mw->img_list ) );
925     if( file )
926     {
927         main_win_open( mw, file, ZOOM_NONE );
928         g_free( file );
929     }
930 }
931 
on_zoom_in(GtkWidget * btn,MainWin * mw)932 void on_zoom_in( GtkWidget* btn, MainWin* mw )
933 {
934     double scale = mw->scale;
935     scale *= 1.05;
936     main_win_set_zoom_scale(mw, scale);
937 }
938 
on_zoom_out(GtkWidget * btn,MainWin * mw)939 void on_zoom_out( GtkWidget* btn, MainWin* mw )
940 {
941     double scale = mw->scale;
942     scale /= 1.05;
943     main_win_set_zoom_scale(mw, scale);
944 }
945 
on_preference(GtkWidget * btn,MainWin * mw)946 void on_preference( GtkWidget* btn, MainWin* mw )
947 {
948     edit_preferences( (GtkWindow*)mw );
949 }
950 
on_quit(GtkWidget * btn,MainWin * mw)951 void on_quit( GtkWidget* btn, MainWin* mw )
952 {
953     cancel_slideshow(mw);
954     gtk_widget_destroy( (GtkWidget*)mw );
955 }
956 
on_button_press(GtkWidget * widget,GdkEventButton * evt,MainWin * mw)957 gboolean on_button_press( GtkWidget* widget, GdkEventButton* evt, MainWin* mw )
958 {
959 #if GTK_CHECK_VERSION(2, 14, 0)
960     if( ! gtk_widget_has_focus( widget ) )
961 #else
962     if( ! GTK_WIDGET_HAS_FOCUS( widget ) )
963 #endif
964         gtk_widget_grab_focus( widget );
965 
966     if( evt->type == GDK_BUTTON_PRESS)
967     {
968         if( evt->button == 1 )    // left button
969         {
970             if( ! mw->pix )
971                 return FALSE;
972             mw->dragging = TRUE;
973             gtk_widget_get_pointer( (GtkWidget*)mw, &mw->drag_old_x ,&mw->drag_old_y );
974             gdk_window_set_cursor( gtk_widget_get_window(widget), mw->hand_cursor );
975         }
976         else if( evt->button == 3 )   // right button
977         {
978             show_popup_menu( mw, evt );
979         }
980     }
981     else if( evt->type == GDK_2BUTTON_PRESS && evt->button == 1 )    // double clicked
982     {
983          on_full_screen( NULL, mw );
984     }
985     return FALSE;
986 }
987 
on_mouse_move(GtkWidget * widget,GdkEventMotion * evt,MainWin * mw)988 gboolean on_mouse_move( GtkWidget* widget, GdkEventMotion* evt, MainWin* mw )
989 {
990     if( ! mw->dragging )
991         return FALSE;
992 
993     int cur_x, cur_y;
994     gtk_widget_get_pointer( (GtkWidget*)mw, &cur_x ,&cur_y );
995 
996     int dx = (mw->drag_old_x - cur_x);
997     int dy = (mw->drag_old_y - cur_y);
998 
999     GtkAdjustment *hadj, *vadj;
1000     hadj = gtk_scrolled_window_get_hadjustment((GtkScrolledWindow*)mw->scroll);
1001     vadj = gtk_scrolled_window_get_vadjustment((GtkScrolledWindow*)mw->scroll);
1002 
1003     GtkRequisition req;
1004     gtk_widget_size_request( (GtkWidget*)mw->img_view, &req );
1005 
1006 #if GTK_CHECK_VERSION(2, 14, 0)
1007     gdouble hadj_page_size = gtk_adjustment_get_page_size(hadj);
1008     gdouble hadj_lower = gtk_adjustment_get_lower(hadj);
1009     gdouble hadj_upper = gtk_adjustment_get_upper(hadj);
1010 #else
1011     gdouble hadj_page_size = hadj->page_size;
1012     gdouble hadj_lower = hadj->lower;
1013     gdouble hadj_upper = hadj->upper;
1014 #endif
1015 
1016     if( ABS(dx) > 4 )
1017     {
1018         mw->drag_old_x = cur_x;
1019         if( req.width > hadj_page_size )
1020         {
1021             gdouble value = gtk_adjustment_get_value (hadj);
1022             gdouble x = value + dx;
1023             if( x < hadj_lower )
1024                 x = hadj_lower;
1025             else if( (x + hadj_page_size) > hadj_upper )
1026                 x = hadj_upper - hadj_page_size;
1027 
1028             if( x != value )
1029                 gtk_adjustment_set_value (hadj, x );
1030         }
1031     }
1032 
1033 #if GTK_CHECK_VERSION(2, 14, 0)
1034     gdouble vadj_page_size = gtk_adjustment_get_page_size(vadj);
1035     gdouble vadj_lower = gtk_adjustment_get_lower(vadj);
1036     gdouble vadj_upper = gtk_adjustment_get_upper(vadj);
1037 #else
1038     gdouble vadj_page_size = vadj->page_size;
1039     gdouble vadj_lower = vadj->lower;
1040     gdouble vadj_upper = vadj->upper;
1041 #endif
1042 
1043     if( ABS(dy) > 4 )
1044     {
1045         if( req.height > vadj_page_size )
1046         {
1047             mw->drag_old_y = cur_y;
1048             gdouble value = gtk_adjustment_get_value (vadj);
1049             gdouble y = value + dy;
1050             if( y < vadj_lower )
1051                 y = vadj_lower;
1052             else if( (y + vadj_page_size) > vadj_upper )
1053                 y = vadj_upper - vadj_page_size;
1054 
1055             if( y != value )
1056                 gtk_adjustment_set_value (vadj, y );
1057         }
1058     }
1059     return FALSE;
1060 }
1061 
on_button_release(GtkWidget * widget,GdkEventButton * evt,MainWin * mw)1062 gboolean on_button_release( GtkWidget* widget, GdkEventButton* evt, MainWin* mw )
1063 {
1064     mw->dragging = FALSE;
1065     gdk_window_set_cursor( gtk_widget_get_window(widget), NULL );
1066     return FALSE;
1067 }
1068 
on_scroll_event(GtkWidget * widget,GdkEventScroll * evt,MainWin * mw)1069 gboolean on_scroll_event( GtkWidget* widget, GdkEventScroll* evt, MainWin* mw )
1070 {
1071     guint modifiers = gtk_accelerator_get_default_mod_mask();
1072     switch( evt->direction )
1073     {
1074     case GDK_SCROLL_UP:
1075         if ((evt->state & modifiers) == GDK_CONTROL_MASK)
1076             on_zoom_in( NULL, mw );
1077         else
1078             on_prev( NULL, mw );
1079         break;
1080     case GDK_SCROLL_DOWN:
1081         if ((evt->state & modifiers) == GDK_CONTROL_MASK)
1082             on_zoom_out( NULL, mw );
1083         else
1084             on_next( NULL, mw );
1085         break;
1086     case GDK_SCROLL_LEFT:
1087         if( gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL )
1088             on_next( NULL, mw );
1089         else
1090             on_prev( NULL, mw );
1091         break;
1092     case GDK_SCROLL_RIGHT:
1093         if( gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL )
1094             on_prev( NULL, mw );
1095         else
1096             on_next( NULL, mw );
1097         break;
1098     }
1099     return TRUE;
1100 }
1101 
on_key_press_event(GtkWidget * widget,GdkEventKey * key)1102 gboolean on_key_press_event(GtkWidget* widget, GdkEventKey * key)
1103 {
1104     MainWin* mw = (MainWin*)widget;
1105     switch( key->keyval )
1106     {
1107         case GDK_Right:
1108         case GDK_KP_Right:
1109         case GDK_rightarrow:
1110             if( gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL )
1111                 on_prev( NULL, mw );
1112             else
1113                 on_next( NULL, mw );
1114             break;
1115         case GDK_Return:
1116         case GDK_space:
1117         case GDK_Next:
1118         case GDK_KP_Down:
1119         case GDK_Down:
1120         case GDK_downarrow:
1121             on_next( NULL, mw );
1122             break;
1123         case GDK_Left:
1124         case GDK_KP_Left:
1125         case GDK_leftarrow:
1126             if( gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL )
1127                 on_next( NULL, mw );
1128             else
1129                 on_prev( NULL, mw );
1130             break;
1131         case GDK_Prior:
1132         case GDK_BackSpace:
1133         case GDK_KP_Up:
1134         case GDK_Up:
1135         case GDK_uparrow:
1136             on_prev( NULL, mw );
1137             break;
1138         case GDK_w:
1139         case GDK_W:
1140             on_slideshow_menu( NULL, mw );
1141             break;
1142         case GDK_KP_Add:
1143         case GDK_plus:
1144         case GDK_equal:
1145             on_zoom_in( NULL, mw );
1146             break;
1147         case GDK_KP_Subtract:
1148         case GDK_minus:
1149             on_zoom_out( NULL, mw );
1150             break;
1151         case GDK_s:
1152         case GDK_S:
1153             on_save( NULL, mw );
1154             break;
1155         case GDK_a:
1156         case GDK_A:
1157             on_save_as( NULL, mw );
1158             break;
1159         case GDK_l:
1160         case GDK_L:
1161             on_rotate_counterclockwise( NULL, mw );
1162             break;
1163         case GDK_r:
1164         case GDK_R:
1165             on_rotate_clockwise( NULL, mw );
1166             break;
1167         case GDK_f:
1168         case GDK_F:
1169             if( mw->zoom_mode != ZOOM_FIT )
1170                 gtk_button_clicked((GtkButton*)mw->btn_fit );
1171             break;
1172         case GDK_g:
1173         case GDK_G:
1174             if( mw->zoom_mode != ZOOM_ORIG )
1175                 gtk_button_clicked((GtkButton*)mw->btn_orig );
1176             break;
1177         case GDK_h:
1178         case GDK_H:
1179             on_flip_horizontal( NULL, mw );
1180             break;
1181         case GDK_v:
1182         case GDK_V:
1183             on_flip_vertical( NULL, mw );
1184             break;
1185         case GDK_o:
1186         case GDK_O:
1187             on_open( NULL, mw );
1188             break;
1189         case GDK_Delete:
1190         case GDK_d:
1191         case GDK_D:
1192             on_delete( NULL, mw );
1193             break;
1194         case GDK_p:
1195         case GDK_P:
1196             on_preference( NULL, mw );
1197 	    break;
1198         case GDK_t:
1199         case GDK_T:
1200             on_toggle_toolbar( NULL, mw );
1201 	    break;
1202         case GDK_Escape:
1203             if( mw->full_screen )
1204                 on_full_screen( NULL, mw );
1205             else
1206                 on_quit( NULL, mw );
1207             break;
1208         case GDK_q:
1209 	case GDK_Q:
1210             on_quit( NULL, mw );
1211             break;
1212         case GDK_F11:
1213             on_full_screen( NULL, mw );
1214             break;
1215 
1216         default:
1217             GTK_WIDGET_CLASS(main_win_parent_class)->key_press_event( widget, key );
1218     }
1219     return FALSE;
1220 }
1221 
main_win_center_image(MainWin * mw)1222 void main_win_center_image( MainWin* mw )
1223 {
1224     GtkAdjustment *hadj, *vadj;
1225     hadj = gtk_scrolled_window_get_hadjustment((GtkScrolledWindow*)mw->scroll);
1226     vadj = gtk_scrolled_window_get_vadjustment((GtkScrolledWindow*)mw->scroll);
1227 
1228     GtkRequisition req;
1229     gtk_widget_size_request( (GtkWidget*)mw->img_view, &req );
1230 
1231 #if GTK_CHECK_VERSION(2, 14, 0)
1232     gdouble hadj_page_size = gtk_adjustment_get_page_size(hadj);
1233     gdouble hadj_upper = gtk_adjustment_get_upper(hadj);
1234 #else
1235     gdouble hadj_page_size = hadj->page_size;
1236     gdouble hadj_upper = hadj->upper;
1237 #endif
1238 
1239     if( req.width > hadj_page_size )
1240         gtk_adjustment_set_value(hadj, ( hadj_upper - hadj_page_size ) / 2 );
1241 
1242 #if GTK_CHECK_VERSION(2, 14, 0)
1243     gdouble vadj_page_size = gtk_adjustment_get_page_size(vadj);
1244     gdouble vadj_upper = gtk_adjustment_get_upper(vadj);
1245 #else
1246     gdouble vadj_page_size = vadj->page_size;
1247     gdouble vadj_upper = vadj->upper;
1248 #endif
1249 
1250     if( req.height > vadj_page_size )
1251         gtk_adjustment_set_value(vadj, ( vadj_upper - vadj_page_size ) / 2 );
1252 }
1253 
rotate_image(MainWin * mw,int angle)1254 void rotate_image( MainWin* mw, int angle )
1255 {
1256     GdkPixbuf* rpix = NULL;
1257 
1258     if( ! mw->pix )
1259         return;
1260 
1261     if(angle > 0)
1262     {
1263         rpix = gdk_pixbuf_rotate_simple( mw->pix, angle );
1264     }
1265     else
1266     {
1267         if(angle == -90)
1268             rpix = gdk_pixbuf_flip( mw->pix, TRUE );
1269         else if(angle == -180)
1270             rpix = gdk_pixbuf_flip( mw->pix, FALSE );
1271     }
1272 
1273     if (!rpix) {
1274         return;
1275     }
1276 
1277     g_object_unref( mw->pix );
1278 
1279     mw->pix = rpix;
1280     image_view_set_pixbuf( (ImageView*)mw->img_view, mw->pix );
1281 
1282     if( mw->zoom_mode == ZOOM_FIT )
1283         main_win_fit_window_size( mw, FALSE, GDK_INTERP_BILINEAR );
1284 }
1285 
main_win_scale_image(MainWin * mw,double new_scale,GdkInterpType type)1286 gboolean main_win_scale_image( MainWin* mw, double new_scale, GdkInterpType type )
1287 {
1288     if( G_UNLIKELY( new_scale == 1.0 ) )
1289     {
1290         gtk_toggle_button_set_active( (GtkToggleButton*)mw->btn_orig, TRUE );
1291         mw->scale = 1.0;
1292         return TRUE;
1293     }
1294     mw->scale = new_scale;
1295     image_view_set_scale( (ImageView*)mw->img_view, new_scale, type );
1296 
1297     update_title( NULL, mw );
1298 
1299     return TRUE;
1300 }
1301 
save_confirm(MainWin * mw,const char * file_path)1302 gboolean save_confirm( MainWin* mw, const char* file_path )
1303 {
1304     if( g_file_test( file_path, G_FILE_TEST_EXISTS ) )
1305     {
1306         GtkWidget* dlg = gtk_message_dialog_new( (GtkWindow*)mw,
1307                 GTK_DIALOG_MODAL,
1308                 GTK_MESSAGE_QUESTION,
1309                 GTK_BUTTONS_YES_NO,
1310                 _("The file name you selected already exists.\nDo you want to overwrite existing file?\n(Warning: The quality of original image might be lost)") );
1311         if( gtk_dialog_run( (GtkDialog*)dlg ) != GTK_RESPONSE_YES )
1312         {
1313             gtk_widget_destroy( dlg );
1314             return FALSE;
1315         }
1316         gtk_widget_destroy( dlg );
1317     }
1318     return TRUE;
1319 }
1320 
main_win_save(MainWin * mw,const char * file_path,const char * type,gboolean confirm)1321 gboolean main_win_save( MainWin* mw, const char* file_path, const char* type, gboolean confirm )
1322 {
1323     gboolean result1,gdk_save_supported;
1324     GSList *gdk_formats;
1325     GSList *gdk_formats_i;
1326     if( ! mw->pix )
1327         return FALSE;
1328 
1329     /* detect if the current type can be save by gdk_pixbuf_save() */
1330     gdk_save_supported = FALSE;
1331     gdk_formats = gdk_pixbuf_get_formats();
1332     for (gdk_formats_i = gdk_formats; gdk_formats_i;
1333          gdk_formats_i = g_slist_next(gdk_formats_i))
1334     {
1335         GdkPixbufFormat *data;
1336         data = gdk_formats_i->data;
1337         if (gdk_pixbuf_format_is_writable(data))
1338         {
1339             if ( strcmp(type, gdk_pixbuf_format_get_name(data))==0)
1340             {
1341                 gdk_save_supported = TRUE;
1342                 break;
1343             }
1344         }
1345     }
1346     g_slist_free (gdk_formats);
1347 
1348     GError* err = NULL;
1349     if (!gdk_save_supported)
1350     {
1351         main_win_show_error( mw, _("Writing this image format is not supported.") );
1352         return FALSE;
1353     }
1354     if( strcmp( type, "jpeg" ) == 0 )
1355     {
1356         char tmp[32];
1357         g_sprintf(tmp, "%d", pref.jpg_quality);
1358         result1 = gdk_pixbuf_save( mw->pix, file_path, type, &err, "quality", tmp, NULL );
1359     }
1360     else if( strcmp( type, "png" ) == 0 )
1361     {
1362         char tmp[32];
1363         g_sprintf(tmp, "%d", pref.png_compression);
1364         result1 = gdk_pixbuf_save( mw->pix, file_path, type, &err, "compression", tmp, NULL );
1365     }
1366     else
1367         result1 = gdk_pixbuf_save( mw->pix, file_path, type, &err, NULL );
1368     if( ! result1 )
1369     {
1370         main_win_show_error( mw, err->message );
1371         return FALSE;
1372     }
1373     return TRUE;
1374 }
1375 
on_delete(GtkWidget * btn,MainWin * mw)1376 void on_delete( GtkWidget* btn, MainWin* mw )
1377 {
1378     cancel_slideshow(mw);
1379     char* file_path = image_list_get_current_file_path( mw->img_list );
1380     if( file_path )
1381     {
1382         int resp = GTK_RESPONSE_YES;
1383 	if ( pref.ask_before_delete )
1384 	{
1385             GtkWidget* dlg = gtk_message_dialog_new( (GtkWindow*)mw,
1386                     GTK_DIALOG_MODAL,
1387                     GTK_MESSAGE_QUESTION,
1388                     GTK_BUTTONS_YES_NO,
1389                     _("Are you sure you want to delete current file?\n\nWarning: Once deleted, the file cannot be recovered.") );
1390             resp = gtk_dialog_run( (GtkDialog*)dlg );
1391             gtk_widget_destroy( dlg );
1392         }
1393 
1394 	if( resp == GTK_RESPONSE_YES )
1395         {
1396             const char* name = image_list_get_current( mw->img_list );
1397 
1398 	    if( g_unlink( file_path ) != 0 )
1399 		main_win_show_error( mw, g_strerror(errno) );
1400 	    else
1401 	    {
1402 		const char* next_name = image_list_get_next( mw->img_list );
1403 		if( ! next_name )
1404 		    next_name = image_list_get_prev( mw->img_list );
1405 
1406 		if( next_name )
1407 		{
1408 		    char* next_file_path = image_list_get_current_file_path( mw->img_list );
1409 		    main_win_open( mw, next_file_path, ZOOM_FIT );
1410 		    g_free( next_file_path );
1411 		}
1412 
1413 		image_list_remove ( mw->img_list, name );
1414 
1415 		if ( ! next_name )
1416 		{
1417 		    main_win_close( mw );
1418 		    image_list_close( mw->img_list );
1419 		    image_view_set_pixbuf( (ImageView*)mw->img_view, NULL );
1420 		    gtk_window_set_title( (GtkWindow*) mw, _("Image Viewer"));
1421 		}
1422 	    }
1423         }
1424 	g_free( file_path );
1425     }
1426 }
1427 
on_toggle_toolbar(GtkMenuItem * item,MainWin * mw)1428 void on_toggle_toolbar( GtkMenuItem* item, MainWin* mw )
1429 {
1430     pref.show_toolbar = !pref.show_toolbar;
1431 
1432     if (pref.show_toolbar)
1433         gtk_widget_show(gtk_widget_get_parent(mw->nav_bar));
1434     else
1435         gtk_widget_hide(gtk_widget_get_parent(mw->nav_bar));
1436 
1437     save_preferences();
1438 }
1439 
1440 
show_popup_menu(MainWin * mw,GdkEventButton * evt)1441 void show_popup_menu( MainWin* mw, GdkEventButton* evt )
1442 {
1443     static PtkMenuItemEntry menu_def[] =
1444     {
1445         PTK_IMG_MENU_ITEM( N_( "Previous" ), GTK_STOCK_GO_BACK, on_prev, GDK_leftarrow, 0 ),
1446         PTK_IMG_MENU_ITEM( N_( "Next" ), GTK_STOCK_GO_FORWARD, on_next, GDK_rightarrow, 0 ),
1447         PTK_IMG_MENU_ITEM( N_( "Start/Stop Slideshow" ), GTK_STOCK_MEDIA_PLAY, on_slideshow_menu, GDK_W, 0 ),
1448         PTK_SEPARATOR_MENU_ITEM,
1449         PTK_IMG_MENU_ITEM( N_( "Zoom Out" ), GTK_STOCK_ZOOM_OUT, on_zoom_out, GDK_minus, 0 ),
1450         PTK_IMG_MENU_ITEM( N_( "Zoom In" ), GTK_STOCK_ZOOM_IN, on_zoom_in, GDK_plus, 0 ),
1451         PTK_IMG_MENU_ITEM( N_( "Fit Image To Window Size" ), GTK_STOCK_ZOOM_FIT, on_zoom_fit_menu, GDK_F, 0 ),
1452         PTK_IMG_MENU_ITEM( N_( "Original Size" ), GTK_STOCK_ZOOM_100, on_orig_size_menu, GDK_G, 0 ),
1453         PTK_SEPARATOR_MENU_ITEM,
1454         PTK_IMG_MENU_ITEM( N_( "Full Screen" ), GTK_STOCK_FULLSCREEN, on_full_screen, GDK_F11, 0 ),
1455         PTK_SEPARATOR_MENU_ITEM,
1456         PTK_IMG_MENU_ITEM( N_( "Rotate Counterclockwise" ), "object-rotate-left", on_rotate_counterclockwise, GDK_L, 0 ),
1457         PTK_IMG_MENU_ITEM( N_( "Rotate Clockwise" ), "object-rotate-right", on_rotate_clockwise, GDK_R, 0 ),
1458         PTK_IMG_MENU_ITEM( N_( "Flip Horizontal" ), "object-flip-horizontal", on_flip_horizontal, GDK_H, 0 ),
1459         PTK_IMG_MENU_ITEM( N_( "Flip Vertical" ), "object-flip-vertical", on_flip_vertical, GDK_V, 0 ),
1460         PTK_SEPARATOR_MENU_ITEM,
1461         PTK_IMG_MENU_ITEM( N_("Open File"), GTK_STOCK_OPEN, G_CALLBACK(on_open), GDK_O, 0 ),
1462         PTK_IMG_MENU_ITEM( N_("Save File"), GTK_STOCK_SAVE, G_CALLBACK(on_save), GDK_S, 0 ),
1463         PTK_IMG_MENU_ITEM( N_("Save As"), GTK_STOCK_SAVE_AS, G_CALLBACK(on_save_as), GDK_A, 0 ),
1464 //        PTK_IMG_MENU_ITEM( N_("Save As Other Size"), GTK_STOCK_SAVE_AS, G_CALLBACK(on_save_as), GDK_A, 0 ),
1465         PTK_IMG_MENU_ITEM( N_("Delete File"), GTK_STOCK_DELETE, G_CALLBACK(on_delete), GDK_Delete, 0 ),
1466         PTK_SEPARATOR_MENU_ITEM,
1467         PTK_IMG_MENU_ITEM( N_("Preferences"), GTK_STOCK_PREFERENCES, G_CALLBACK(on_preference), GDK_P, 0 ),
1468         PTK_IMG_MENU_ITEM( N_("Show/Hide toolbar"), NULL, G_CALLBACK(on_toggle_toolbar), GDK_T, 0 ),
1469         PTK_STOCK_MENU_ITEM( GTK_STOCK_ABOUT, on_about ),
1470         PTK_SEPARATOR_MENU_ITEM,
1471         PTK_IMG_MENU_ITEM( N_("Quit"), GTK_STOCK_QUIT, G_CALLBACK(on_quit), GDK_Q, 0 ),
1472         PTK_MENU_END
1473     };
1474     GtkWidget* rotate_cw;
1475     GtkWidget* rotate_ccw;
1476     GtkWidget* flip_v;
1477     GtkWidget* flip_h;
1478 
1479     menu_def[10].ret = &rotate_ccw;
1480     menu_def[11].ret = &rotate_cw;
1481     menu_def[12].ret = &flip_h;
1482     menu_def[13].ret = &flip_v;
1483 
1484     // mw accel group is useless. It's only used to display accels in popup menu
1485     GtkAccelGroup* accel_group = gtk_accel_group_new();
1486     GtkMenuShell* popup = (GtkMenuShell*)ptk_menu_new_from_data( menu_def, mw, accel_group );
1487 
1488     if( mw->animation )
1489     {
1490         gtk_widget_set_sensitive( rotate_ccw, FALSE );
1491         gtk_widget_set_sensitive( rotate_cw, FALSE );
1492         gtk_widget_set_sensitive( flip_h, FALSE );
1493         gtk_widget_set_sensitive( flip_v, FALSE );
1494     }
1495 
1496     gtk_widget_show_all( (GtkWidget*)popup );
1497     g_signal_connect( popup, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL );
1498     gtk_menu_popup( (GtkMenu*)popup, NULL, NULL, NULL, NULL, evt->button, evt->time );
1499 }
1500 
on_about(GtkWidget * menu,MainWin * mw)1501 void on_about( GtkWidget* menu, MainWin* mw )
1502 {
1503     GtkWidget * about_dlg;
1504     const gchar *authors[] =
1505     {
1506         "洪任諭 Hong Jen Yee <pcman.tw@gmail.com>",
1507         "Martin Siggel <martinsiggel@googlemail.com>",
1508         "Hialan Liu <hialan.liu@gmail.com>",
1509         "Marty Jack <martyj19@comcast.net>",
1510         "Louis Casillas <oxaric@gmail.com>",
1511         "Will Davies",
1512         _(" * Refer to source code of EOG image viewer and GThumb"),
1513         NULL
1514     };
1515     /* TRANSLATORS: Replace this string with your names, one name per line. */
1516     gchar *translators = _( "translator-credits" );
1517 
1518     about_dlg = gtk_about_dialog_new ();
1519 
1520     gtk_container_set_border_width ( ( GtkContainer*)about_dlg , 2 );
1521     gtk_about_dialog_set_version ( (GtkAboutDialog*)about_dlg, VERSION );
1522     gtk_about_dialog_set_program_name ( (GtkAboutDialog*)about_dlg, _( "GPicView" ) );
1523     gtk_about_dialog_set_logo_icon_name ( (GtkAboutDialog*)about_dlg, "gpicview" );
1524     gtk_about_dialog_set_copyright ( (GtkAboutDialog*)about_dlg, _( "Copyright (C) 2007 - 2011" ) );
1525     gtk_about_dialog_set_comments ( (GtkAboutDialog*)about_dlg, _( "Lightweight image viewer from LXDE project" ) );
1526     gtk_about_dialog_set_license ( (GtkAboutDialog*)about_dlg, "GPicView\n\nCopyright (C) 2007 Hong Jen Yee (PCMan)\n\nThis program is free software; you can redistribute it and/or\nmodify it under the terms of the GNU General Public License\nas published by the Free Software Foundation; either version 2\nof the License, or (at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program; if not, write to the Free Software\nFoundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA." );
1527     gtk_about_dialog_set_website ( (GtkAboutDialog*)about_dlg, "http://wiki.lxde.org/en/GPicView" );
1528     gtk_about_dialog_set_authors ( (GtkAboutDialog*)about_dlg, authors );
1529     gtk_about_dialog_set_translator_credits ( (GtkAboutDialog*)about_dlg, translators );
1530     gtk_window_set_transient_for( (GtkWindow*) about_dlg, GTK_WINDOW( mw ) );
1531 
1532     gtk_dialog_run( ( GtkDialog*)about_dlg );
1533     gtk_widget_destroy( about_dlg );
1534 }
1535 
on_drag_data_received(GtkWidget * widget,GdkDragContext * drag_context,int x,int y,GtkSelectionData * data,guint info,guint time,MainWin * mw)1536 void on_drag_data_received( GtkWidget* widget, GdkDragContext *drag_context,
1537                 int x, int y, GtkSelectionData* data, guint info, guint time, MainWin* mw )
1538 {
1539     if( !data)
1540         return;
1541 
1542 #if GTK_CHECK_VERSION(2, 14, 0)
1543     int data_length = gtk_selection_data_get_length(data);
1544 #else
1545     int data_length = data->length;
1546 #endif
1547 
1548     if( data_length <= 0)
1549         return;
1550 
1551     char* file = NULL;
1552     if( info == 0 )    // text/uri-list
1553     {
1554         char** uris = gtk_selection_data_get_uris( data );
1555         if( uris )
1556         {
1557             file = g_filename_from_uri(*uris, NULL, NULL);
1558             g_strfreev( uris );
1559         }
1560     }
1561     else if( info == 1 )    // text/plain
1562     {
1563         file = (char*)gtk_selection_data_get_text( data );
1564     }
1565     if( file )
1566     {
1567         main_win_open( mw, file, ZOOM_FIT );
1568         g_free( file );
1569     }
1570 }
1571 
main_win_set_zoom_scale(MainWin * mw,double scale)1572 static void main_win_set_zoom_scale(MainWin* mw, double scale)
1573 {
1574     main_win_set_zoom_mode(mw, ZOOM_SCALE);
1575 
1576     if (scale > 20.0)
1577         scale = 20.0;
1578     if (scale < 0.02)
1579         scale = 0.02;
1580 
1581     if (mw->scale != scale)
1582         main_win_scale_image(mw, scale, GDK_INTERP_BILINEAR);
1583 }
1584 
main_win_set_zoom_mode(MainWin * mw,ZoomMode mode)1585 static void main_win_set_zoom_mode(MainWin* mw, ZoomMode mode)
1586 {
1587     if (mw->zoom_mode == mode)
1588        return;
1589 
1590     mw->zoom_mode = mode;
1591 
1592     main_win_update_zoom_buttons_state(mw);
1593 
1594     if (mode == ZOOM_ORIG)
1595     {
1596         mw->scale = 1.0;
1597         if (!mw->pix)
1598            return;
1599 
1600         update_title(NULL, mw);
1601 
1602         image_view_set_scale( (ImageView*)mw->img_view, 1.0, GDK_INTERP_BILINEAR );
1603 
1604         while (gtk_events_pending ())
1605             gtk_main_iteration ();
1606 
1607         main_win_center_image( mw ); // FIXME:  mw doesn't work well. Why?
1608     }
1609     else if (mode == ZOOM_FIT)
1610     {
1611         main_win_fit_window_size( mw, FALSE, GDK_INTERP_BILINEAR );
1612     }
1613 }
1614 
main_win_update_zoom_buttons_state(MainWin * mw)1615 static void main_win_update_zoom_buttons_state(MainWin* mw)
1616 {
1617     gboolean button_fit_active = mw->zoom_mode == ZOOM_FIT;
1618     gboolean button_orig_active = mw->zoom_mode == ZOOM_ORIG;
1619 
1620     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(mw->btn_fit)) != button_fit_active)
1621         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mw->btn_fit), button_fit_active);
1622 
1623     if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(mw->btn_orig)) != button_orig_active)
1624         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(mw->btn_orig), button_orig_active);
1625 }
1626 
1627 
1628