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